<?php
class id3{
/*
* By Adrien Gibrat <hide@address.com> based on work of Sandy McArthur,Jr. <hide@address.com>
*
* This class can read/write ID3v1 et ID3v1.1 informations in any mp3
* ID3v2 are not supported, too complex...
*
* The methods designed for general use are readframe() and write(),
* Please read the comment before each method for the specifics of each.
*
* eg:
* require_once('class.id3.php');
* $id3 = new id3('/path/to/file.mp3');
* echo $id3->id3[artist],' - ',$id3->id3[name];
* $id3->id3[comment] = 'not more 30 chars';
* $id3->write();
* The original version is available at:
* http://Leknor.com/code/
*
*/
var $file = false; // mp3/mpeg file name
// ID3v1.1 Fields:
var $id3 = array('name'=>'','artist'=>'','album'=>'','year'=>'','comment'=>'','track'=>0,'genre'=>'','genreno'=>255);
// MP3 Frame Stuff
var $info = array('mpeg_ver'=>false,'layer'=>false,'bitrate'=>false,'filesize'=>false,'length'=>false);
var $errors = array('no_open'=>'Impossible d\'ouvrir le fichier ',
'no_modif'=>'Impossible de modifier les Informations ID3 du fichier ',
'no_add'=>'Impossible d\'ajouter les Informations ID3 au fichier ',
'no_ID3read'=>'Impossible de lire les Informations ID3 du fichier ',
'no_ID3'=>'Aucune Information ID3 dans le fichier ',
'no_frame'=>' est un fichier non valide! ');
var $error = false; // if any errors they will be here
var $bitrates = array(
'1'=>array('1' => array(0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
'2' => array(0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0),
'3' => array(0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0)),
'2'=>array('1' => array(0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
'2' => array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0),
'3' => array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0)),
'2.5'=>array('1' => array(0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
'2' => array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0),
'3' => array(0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0)));
var $genres = array("Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B",
"Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop",
"Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","Alternative Rock",
"Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial",
"Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta","Top 40","Christian Rap","Pop/Funk","Jungle",
"Native US","Cabaret","New Wave","Psychadelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro",
"Musical","Rock & Roll","Hard Rock","Folk","Folk-Rock","National Folk","Swing","Fast Fusion","Bebob","Latin","Revival","Celtic","Bluegrass",
"Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic",
"Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club",
"Tango","Samba","Folklore","Ballad","Power Ballad","Rhytmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","Acapella","Euro-House",
"Dance Hall","Goa","Drum & Bass","Club-House","Hardcore","Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap",
"Heavy Metal","Black Metal","Crossover","Contemporary Christian","Christian Rock","Merengue","Salsa","Trash Metal","Anime","Jpop","Synthpop");
/* id3 constructor - creates a new id3 object and maybe loads a tag
* from a file.
*
* $file - the path to the mp3/mpeg file. When in doubt use a full path.
* $readframe -(Optional) - study the mpeg frame to get extra info like bitrate and length
* You should advoid studing alot of files as it will siginficantly slow this down.
*/
function id3($file,$readframe = false){
$this->file = $file;
$this->_read();
if($readframe)
$this->readframe();
} // id3($file)
/* write - update the id3v1 tags on the file.
*
* if there is an error it will return false and a message will be
* put in $this->error
*/
function write(){
if(!($f = @fopen($this->file,'r+b'))){
$this->error = $this->errors[no_open].$this->file;
return false;
}
if(fseek($f,-128,SEEK_END)==-1){
$this->error = $this->errors[no_modif].$this->file;
return false;
}
$this->id3[genreno] = $this->getgenreno($this->id3[genre],$this->id3[genreno]);
$newtag = $this->_encode();
$r = fread($f,128);
if($this->_decode($r)){
if(fseek($f,-128,SEEK_END)==-1){
$this->error = $this->errors[no_modif].$this->file;
return false;
}
fwrite($f,$newtag);
}else{
if(fseek($f,0,SEEK_END)==-1){
$this->error = $this->errors[no_add].$this->file;
return false;
}
fwrite($f,$newtag);
}
fclose($f);
} // write()
/* _read - read a ID3 v1 or v1.1 tag from a file
*
* $file should be the path to the mp3 to look for a tag.
* When in doubt use the full path.
*
* if there is an error it will return false and a message will be
* put in $this->error
*/
function _read(){
if(!($f = @fopen($this->file,'rb'))){
$this->error = $this->errors[no_open].$this->file;
return false;
}
if(fseek($f,-128,SEEK_END)==-1){
$this->error = $this->errors[no_ID3read].$this->file;
return false;
}
$r = fread($f,128);
fclose($f);
$id3tag = $this->_decode($r);
if($id3tag){
$tmp = explode(Chr(0),$id3tag['NAME']);
$this->id3[name] = $tmp[0];
$tmp = explode(Chr(0),$id3tag['ARTISTS']);
$this->id3[artist] = $tmp[0];
$tmp = explode(Chr(0),$id3tag['ALBUM']);
$this->id3[album] = $tmp[0];
$tmp = explode(Chr(0),$id3tag['YEAR']);
$this->id3[year] = $tmp[0];
$tmp = explode(Chr(0),$id3tag['COMMENT']);
$this->id3[comment] = $tmp[0];
if(isset($id3tag['TRACK']))
$this->id3[track] = $id3tag['TRACK'];
$this->id3[genreno] = $id3tag['GENRENO'];
$this->id3[genre] = $id3tag['GENRE'];
}
} // _read()
/* _decode - decodes that ID3v1 or ID3v1.1 tag
*
* false will be returned if there was an error decoding the tag
* else an array will be returned
*/
function _decode($rawtag){
$format =($rawtag[125]==Chr(0)and$rawtag[126]!=Chr(0))?'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a28COMMENT/x1/C1TRACK/C1GENRENO':'a3TAG/a30NAME/a30ARTISTS/a30ALBUM/a4YEAR/a30COMMENT/C1GENRENO';
$id3tag = unpack($format,$rawtag);
if($id3tag['TAG']=='TAG')
$id3tag['GENRE'] = $this->genres[$id3tag['GENRENO']];
else{
$this->error = $this->errors[no_ID3].$this->file;
$id3tag = false;
}
return $id3tag;
} // _decode()
/* _encode - encode the ID3 tag
*
* the newly built tag will be returned
*/
function _encode(){
$newtag = ($this->id3[track])?pack('a3a30a30a30a4a28x1C1C1','TAG',$this->id3[name],$this->id3[artist],$this->id3[album],$this->id3[year],$this->id3[comment],$this->id3[track],$this->id3[genreno]):pack('a3a30a30a30a4a30C1','TAG',$this->id3[name],$this->id3[artist],$this->id3[album],$this->id3[year],$this->id3[comment],$this->id3[genreno]);
return $newtag;
} // _encode()
/* getgenreno - return the number of the genre name
*
* the genre number is returned or 0xff(255) if a match is not found
* you can specify the default genreno to use if one is not found
* no error message is ever returned
*/
function getgenreno($genre,$default = 0xff){
$this->id3[genreno] = false;
if($genre)
foreach($this->genres as $no => $name)
if(strtolower($genre)==strtolower($name))
$this->id3[genreno] = $no;
if($this->id3[genreno]===false)
$this->id3[genreno] = $default;
return $this->id3[genreno];
} // getgenreno($genre,$default = 0xff)
/* readframe - does extra work to get the MPEG frame info
*
* set $this->info[mpeg_ver], $this->info[layer], $this->info[bitrate], $this->info[filesize] and $this->info[length]
*/
function readframe(){
if(!($f = @fopen($this->file,'rb'))){
$this->error = $this->errors[no_open].$this->file;
return false;
}
$this->info[filesize] = filesize($this->file);
do{
while(fread($f,1)!=Chr(255)){ // Find the first frame
if(feof($f)){
$this->error = $this->file.$this->errors[no_frame];
return false;
}
}
fseek($f,ftell($f)-1); // back up one byte
$r = fread($f,4);
$bits = unpack('H*bits',$r);
$bits = base_convert($bits['bits'],16,2);
} while(!$bits[8]and!$bits[9]and!$bits[10]); // 1st 8 bits true from the while
fclose($f);
if($bits[11]==0)
$this->info[mpeg_ver] = "2.5";
else if($bits[12]==0)
$this->info[mpeg_ver] = "2";
else
$this->info[mpeg_ver] = "1";
$layer = array(array(0,3),array(2,1));
$this->info[layer] = $layer[$bits[13]][$bits[14]];
$bitrate = 0;
if($bits[16]==1)
$bitrate += 8;
if($bits[17]==1)
$bitrate += 4;
if($bits[18]==1)
$bitrate += 2;
if($bits[19]==1)
$bitrate += 1;
$this->info[bitrate] = $this->bitrates[$this->info[mpeg_ver]][$this->info[layer]][$bitrate];
$s = ($this->info[bitrate]==0)?-1:8*filesize($this->file)/1000/$this->info[bitrate];
$this->info[length] = sprintf('%02d:%02d:%02d',floor($s/3600)*3600,floor($s/60)-floor($s/3600)*3600,floor($s-floor($s/60)*60-floor($s/3600)*3600));
} // _readframe()
} // end of id3
?>