Location: PHPKode > scripts > Read/Write ID3v1 > class.id3.php
<?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
?>
Return current item: Read/Write ID3v1