# Bildinformation aus id3v2-Tag auslesen



## Html (6. August 2007)

Hallo, möchte gerne die id3v2-Informationen aus mp3-Dateien auslesen. Habe dazu folgenden Code gefunden:


```
<?php

/*
//===================================================\\
|                                                     |
|               ID-Tag Reader by Ansi                 |
|                                                     |
|          Reading IDv1.1 and IDv4.0 Tags             |
|                                                     |
|     if you want more frames to be read consult      |
|        http://www.id3.org/id3v2.3.0.html            |
|                                                     |
|                                                     |
\\===================================================//
*/

function readHeader($mp3){

// -------> Config <-------

$unuebliche_tags = true;    //gibt auch Tags aus, die von der üblichen Software *nicht* genutzt werden

// -------> Config <-------

    $header = array();

    //Liste der genutzen Frames für ID3v2.3 und deren
    //Rückgabe im $header[]- Array. Um Übersichtlichkeit
    //zu wahren werden alle Frames, die nicht in dieser
    //Liste stehen standardmäßig später *nicht* ausgegeben.
    $frames = array(
        'TPE1'    =>    'artist',
        'TALB'    =>    'album',
        'COMM'    =>    'comment',
        'TBPM'    =>    'bitrate',
        'TCON'    =>    'genre',
        'TCOM'    =>    'composer',
        'TDAT'    =>    'date',
        'TENC'    =>    'encoder',
        'TIT2'    =>    'title',
        'TIT3'    =>    'subtitle',
        'TLAN'    =>    'languages',
        'TLEN'    =>    'length',
        'TYER'    =>    'year',
        'TRCK'    =>    'track',
        'TORY'    =>    'year',
        'TCOP'    =>    'copyright');
    
    //Liste der Genres und ihrer Entsprechung
    $genres = array(
        0   => 'Blues',
        1   => 'Classic Rock',
        2   => 'Country',
        3   => 'Dance',
        4   => 'Disco',
        5   => 'Funk',
        6   => 'Grunge',
        7   => 'Hip-Hop',
        8   => 'Jazz',
        9   => 'Metal',
        10  => 'New Age',
        11  => 'Oldies',
        12  => 'Other',
        13  => 'Pop',
        14  => 'R&B',
        15  => 'Rap',
        16  => 'Reggae',
        17  => 'Rock',
        18  => 'Techno',
        19  => 'Industrial',
        20  => 'Alternative',
        21  => 'Ska',
        22  => 'Death Metal',
        23  => 'Pranks',
        24  => 'Soundtrack',
        25  => 'Euro-Techno',
        26  => 'Ambient',
        27  => 'Trip-Hop',
        28  => 'Vocal',
        29  => 'Jazz+Funk',
        30  => 'Fusion',
        31  => 'Trance',
        32  => 'Classical',
        33  => 'Instrumental',
        34  => 'Acid',
        35  => 'House',
        36  => 'Game',
        37  => 'Sound Clip',
        38  => 'Gospel',
        39  => 'Noise',
        40  => 'Alternative Rock',
        41  => 'Bass',
        42  => 'Soul',
        43  => 'Punk',
        44  => 'Space',
        45  => 'Meditative',
        46  => 'Instrumental Pop',
        47  => 'Instrumental Rock',
        48  => 'Ethnic',
        49  => 'Gothic',
        50  => 'Darkwave',
        51  => 'Techno-Industrial',
        52  => 'Electronic',
        53  => 'Pop-Folk',
        54  => 'Eurodance',
        55  => 'Dream',
        56  => 'Southern Rock',
        57  => 'Comedy',
        58  => 'Cult',
        59  => 'Gangsta',
        60  => 'Top 40',
        61  => 'Christian Rap',
        62  => 'Pop/Funk',
        63  => 'Jungle',
        64  => 'Native US',
        65  => 'Cabaret',
        66  => 'New Wave',
        67  => 'Psychadelic',
        68  => 'Rave',
        69  => 'Showtunes',
        70  => 'Trailer',
        71  => 'Lo-Fi',
        72  => 'Tribal',
        73  => 'Acid Punk',
        74  => 'Acid Jazz',
        75  => 'Polka',
        76  => 'Retro',
        77  => 'Musical',
        78  => 'Rock & Roll',
        79  => 'Hard Rock',
        80  => 'Folk',
        81  => 'Folk-Rock',
        82  => 'National Folk',
        83  => 'Swing',
        84  => 'Fast Fusion',
        85  => 'Bebob',
        86  => 'Latin',
        87  => 'Revival',
        88  => 'Celtic',
        89  => 'Bluegrass',
        90  => 'Avantgarde',
        91  => 'Gothic Rock',
        92  => 'Progressive Rock',
        93  => 'Psychedelic Rock',
        94  => 'Symphonic Rock',
        95  => 'Slow Rock',
        96  => 'Big Band',
        97  => 'Chorus',
        98  => 'Easy Listening',
        99  => 'Acoustic',
        100 => 'Humour',
        101 => 'Speech',
        102 => 'Chanson',
        103 => 'Opera',
        104 => 'Chamber Music',
        105 => 'Sonata',
        106 => 'Symphony',
        107 => 'Booty Bass',
        108 => 'Primus',
        109 => 'Porn Groove',
        110 => 'Satire',
        111 => 'Slow Jam',
        112 => 'Club',
        113 => 'Tango',
        114 => 'Samba',
        115 => 'Folklore',
        116 => 'Ballad',
        117 => 'Power Ballad',
        118 => 'Rhytmic Soul',
        119 => 'Freestyle',
        120 => 'Duet',
        121 => 'Punk Rock',
        122 => 'Drum Solo',
        123 => 'Acapella',
        124 => 'Euro-House',
        125 => 'Dance Hall',
        126 => 'Goa',
        127 => 'Drum & Bass',
        128 => 'Club-House',
        129 => 'Hardcore',
        130 => 'Terror',
        131 => 'Indie',
        132 => 'BritPop',
        133 => 'Negerpunk',
        134 => 'Polsk Punk',
        135 => 'Beat',
        136 => 'Christian Gangsta Rap',
        137 => 'Heavy Metal',
        138 => 'Black Metal',
        139 => 'Crossover',
        140 => 'Contemporary Christian',
        141 => 'Christian Rock',
        142 => 'Merengue',
        143 => 'Salsa',
        144 => 'Trash Metal',
        145 => 'Anime',
        146 => 'Jpop',
        147 => 'Synthpop');

    //MP3 einlesen
    $filelength = filesize($mp3);
    $hdl = fopen($mp3, "rb");
    if($hdl):
            $content .= fread($hdl, $filelength);
        fclose($hdl);
    endif;

    //Versuche ID3v2 Tag zu finden
    $IDTag = strpos($content, "ID3");

    //Wenn ID3v2 nicht gefunden wird nach ID3v1
    //suchen, entspricht TAG
    if($IDTag === false):
        $IDTag = strpos($content, "TAG");        
        if($IDTag === false):
            $header['type'] = "kein Header gefunden";
            return $header;
            exit;
        else:
            $header['type'] = "ID3v1";
            //$header['pos'] = $IDTag;
        endif;
    else:
        $header['type'] = "ID3v2";
        //$header['pos'] = $IDTag;
    endif;

    switch ($header['type']):
        //ID3v2 Tags auslesen: Flexiblere Felder
        case "ID3v2":
            //10 Byte des ID3 Headers auslesen
            $ID3Header = substr($content, $IDTag, 10);
            
            //Exakte Version der genutzen ID3v2 Version ermitteln
            $header['type'] .= ".".ord(substr($ID3Header, 3, 1)).".".ord(substr($ID3Header, 4, 1));
            
            //Gesamte Headerlänge inklusive Frames ermitteln
            $HeaderLength =  265 * 8 * 8 * ord(substr($ID3Header, 6, 1)) + 265 * 8 * ord(substr($ID3Header, 7, 1)) + 265 *  ord(substr($ID3Header,  8, 1)) + ord(substr($ID3Header,  9, 1));
            
            //ID3 Header in Variabel ablegen, $content leeren
            $ID3Header = substr($content, $IDTrag, $HeaderLength + 10);
            $content = "";

            //Startposition für Frameerkennung ist Byte 10
            $CurPos = 10;
            
            //So lange Frames suchen / auslesen, bis eine Frameposition
            //leer ist oder der Header durchlaufen wurde
            while($CurPos < $HeaderLength + 10):
                $FrameName = substr($ID3Header, $CurPos, 4);
                if(trim($FrameName) != ""):
                    $FrameLength = 265 * 8 * 8 * ord(substr($ID3Header, $CurPos + 4, 1)) + 265 * 8 * ord(substr($ID3Header, $CurPos + 5, 1)) + 265 *  ord(substr($ID3Header,  $CurPos + 6, 1)) + ord(substr($ID3Header,  $CurPos + 7, 1));
                    $FrameContent = substr($ID3Header, $CurPos + 10, $FrameLength);
                    $CurPos = $CurPos + $FrameLength + 10;
                    if(array_key_exists($FrameName, $frames) == true):
                        $header[$frames[$FrameName]] = $FrameContent;
                    elseif($unuebliche_tags == true):    
                        $counter++;
                        $header[$FrameName."_".$counter] = $FrameContent;
                    endif;
                else:
                    break;
                endif;
            endwhile;
            break;

        //Hier wird der ID3V1.1 Standard vorausgesetzt,
        //der im letzten Byte des Kommentares (127) den Track speichert
        case "ID3v1.1":
            $header['titel'] = trim(substr($content, $IDTag + 3, 30));
            $header['artist'] = trim(substr($content, $IDTag + 33, 30));
            $header['album'] = trim(substr($content, $IDTag + 63, 30));
            $header['year'] = trim(substr($content, $IDTag + 93, 4));
            $header['comment'] = trim(substr($content, $IDTag + 97, 29));
            $header['track'] = ord(trim(substr($content, $IDTag + 126, 1)));
            $header['genre'] = ord(trim(substr($content, $IDTag + 127, 1)));
            if(!$header['genre'] === false):
                $header['genre'] = $genres[$header['genre']];
            endif;
            break;
    endswitch;

    return $header;

}

// Tabelle für ID- Tags generieren

echo "<table><tr><th align = 'left'>Feld</th><th align = 'left'>Inhalt</th></tr>";

    foreach (readHeader('Add N To [X] - 04 - kingdom of shades.mp3') as $key => $value){
        echo "<tr><td>$key</td><td>$value</td></tr>";
    }

echo "</table>";?>
```


Der Code funktioniert zwar super bis auf eine Kleinigkeit. Und zwar ist es ja möglich im id3v2 die Cover bzw Inlets mit anzugeben. Doch beim auslesen mit obigem Code eines solchen id3v2-Tags stehen dann unter den allgemeinen Informationen (album, interpret etc...) nur lauter seltsame Zeichen. Denke das sollen die Bildinformationen sein. Aber damit kann ich nix anfangen.

Lässt sich das irgendwie in brauchbare Informationen wandeln. Welche Informationen werden zu den Bildern überhaupt in dem id3v2 tag gespeichert.


mfg,Olli


----------



## Flex (6. August 2007)

Du müsstest ein externes Skript benutzen, dass den Content als Bilddatei ausweist.

Sagen wir, das Bild kommt als JPEG an, müsste es ungefähr so aussehen:


```
<!-- Einbinden -->
<img src="./bild.php?pic=<?php echo urlencode($var); ?>" />
```

Und die bild.php müsste ungefähr so aussehen:


```
<?php
$pic = urldecode($_GET['var']);
header('Content-Type: image/jpeg');
print($var);
?>
```


----------



## Html (6. August 2007)

Erstmal danke für deine Antwort... 

Was ich lediglich brauche ist eigentlich der Pfad des Bildes.

Ich benötige zum Schluss einfach mehrere Variablen:

$artist
$album
$genre
$Pfad zum Coverbild

die ich dann einfach in eine Datenbank schreiben kann.

Das einzige was mir halt Probleme macht ist den Pfad zum zugehörigen Cover in einer Variable zu speichern.

Das Auslesen vom Rest funktioniert ja und das übertragen in DB weiß ich auch wie das geht. 

Vielleicht kann mir da jemand nochmal das ganze etwas genauer posten. Sorry aber bin phpnewbie


*//EDIT*

Hallo habe das ganze jetzt mal so in den o.g Code eingebunden:



```
.......

$ausgabe=readHeader('LeAnn Rimes\2004 The Best Of\04 - LeAnn Rimes - I Need You.mp3');

echo $ausgabe["type"]."<br>";
echo $ausgabe["artist"]."<br>";
echo $ausgabe["title"]."<br>";
echo $ausgabe["album"]."<br>";

//echo $ausgabe["APIC_1"]."<br>";
//print_r($ausgabe);

echo "<img src=bild.php?var=".urlencode($ausgabe["APIC_1"])." />" ;
```


Und hier meine bild.php


```
<?php


$pic = urldecode($_GET['var']);
header('Content-Type: image/jpeg');
print($pic);



?>
```


Doch leider zeigt mir der Browser kein Bild an sondern nur ein Platzhalter.


Warum?


mfg,Olli


----------



## Flex (7. August 2007)

Könntest du mir mal eine solche MP3 mal schicken?
Aus irgendeinem Grund kann ich in WinAmp dieses Feld nicht entdecken, noch hat irgendeine meiner MP3s dieses Feld drin.
E-Mail hast du als PN.


----------



## Watusimann (7. August 2007)

[OFF] ...
Mal so nebenbei ... ich tummel mich ja viel in Musiksachen rum, aber habe noch NIE einen Genré gefunden, der mit "Negerpunk (siehe  $genres = array(  ... 133))" bezeichnet wurde 
[/OFF]


----------



## Flex (7. August 2007)

Watusimann hat gesagt.:


> [OFF] ...
> Mal so nebenbei ... ich tummel mich ja viel in Musiksachen rum, aber habe noch NIE einen Genré gefunden, der mit "Negerpunk (siehe  $genres = array(  ... 133))" bezeichnet wurde
> [/OFF]



http://en.wikipedia.org/wiki/Negerpunk

Eher ein Joke als eine wirkliche Sparte. 
Wenn auch eher ein typischer LowBudget US Joke


----------



## Flo<H> (7. August 2007)

Html hat gesagt.:


> Der Code funktioniert zwar super bis auf eine Kleinigkeit. Und zwar ist es ja möglich im id3v2 die Cover bzw Inlets mit anzugeben. Doch beim auslesen mit obigem Code eines solchen id3v2-Tags stehen dann unter den allgemeinen Informationen (album, interpret etc...) nur lauter seltsame Zeichen. Denke das sollen die Bildinformationen sein. Aber damit kann ich nix anfangen.



Hallo!
Hab zwar ID3Tags bisher nur in Verbindung mit Java bearbeitet, aber ich habe eine Vermutung warum du nur lauter seltsame Zeichen erhältst: bei ID3Tags der Version 2 ist es möglich den Zeichensatz selber zu wählen. Mögliche Zeichensätze waren Iso-Latin-1, UTF-8 und zwei UTF-16 Zeichensätze glaub ich. Wenn du also einen Wert des Tags ausliest, aber im falschen Zeichensatz darstellst erhältst du eben soeinen Zeichensalat. Wie gesagt ist nur eine Vermutung, am besten nimmst du eine MP3, löscht sämtliche ID3-Tags und speicherst dann nur den ID3v2 Tag. Mit dieser kannst du dann Testen ob das Skript überhaupt richtig funktioniert. Bein Überfliegen habe ich nämlich niergends eine Konvertierung des Zeichensatzes gefunden. 
By the way: mit Hilfe eines Hex-Editors ist es auch möglich sich die Tags anzuschauen. 
Btw2: viele Mp3s mit Bild habe ich nicht, aber in irgendeinem Editor ist mir mal eine Mp3 mit Bild aufgefallen. Dieses muss wohl mit in der Mp3 gespeichert gewesen sein.

mfg flo


----------



## Marvin Schmidt (8. August 2007)

Hallo Olli,



Html hat gesagt.:


> Der Code funktioniert zwar super bis auf eine Kleinigkeit. Und zwar ist es ja möglich im id3v2 die Cover bzw Inlets mit anzugeben. Doch beim auslesen mit obigem Code eines solchen id3v2-Tags stehen dann unter den allgemeinen Informationen (album, interpret etc...) nur lauter seltsame Zeichen. Denke das sollen die Bildinformationen sein. Aber damit kann ich nix anfangen.


Ja, das sind die Bildinformationen. Bilder (außer SVG) sind Binärdateien. Binärdateien verwenden 8 Bit, wodurch Bytewerte außerhalb des ASCII-Bereichs (0-127) auftreten können. Dazu kommt, dass der ASCII-Zeichensatz selber 33 nicht-druckbare Zeichen enthält.



Html hat gesagt.:


> Lässt sich das irgendwie in brauchbare Informationen wandeln. Welche Informationen werden zu den Bildern überhaupt in dem id3v2 tag gespeichert.


Das steht alles in der Spezifikation des ID3-Tags:

```
4.14.   Attached picture

   This frame contains a picture directly related to the audio file.
   Image format is the MIME type and subtype [MIME] for the image. In
   the event that the MIME media type name is omitted, "image/" will be
   implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format
   should be used when interoperability is wanted. Description is a
   short description of the picture, represented as a terminated
   text string. There may be several pictures attached to one file, each
   in their individual "APIC" frame, but only one with the same content
   descriptor. There may only be one picture with the picture type
   declared as picture type $01 and $02 respectively. There is the
   possibility to put only a link to the image file by using the 'MIME
   type' "-->" and having a complete URL [URL] instead of picture data.
   The use of linked files should however be used sparingly since there
   is the risk of separation of files.

     <Header for 'Attached picture', ID: "APIC">
     Text encoding      $xx
     MIME type          <text string> $00
     Picture type       $xx
     Description        <text string according to encoding> $00 (00)
     Picture data       <binary data>


   Picture type:  $00  Other
                  $01  32x32 pixels 'file icon' (PNG only)
                  $02  Other file icon
                  $03  Cover (front)
                  $04  Cover (back)
                  $05  Leaflet page
                  $06  Media (e.g. label side of CD)
                  $07  Lead artist/lead performer/soloist
                  $08  Artist/performer
                  $09  Conductor
                  $0A  Band/Orchestra
                  $0B  Composer
                  $0C  Lyricist/text writer
                  $0D  Recording Location
                  $0E  During recording
                  $0F  During performance
                  $10  Movie/video screen capture
                  $11  A bright coloured fish
                  $12  Illustration
                  $13  Band/artist logotype
                  $14  Publisher/Studio logotype
```


Hier eine Implementierung, die alle Angaben ausliest und das Bild in eine Datei schreibt:

```
while($CurPos < $HeaderLength + 10):
	$FrameName = substr($ID3Header, $CurPos, 4);
	if(trim($FrameName) != ""):
		$FrameLength = toNumber(substr($ID3Header, $CurPos+4, 4));
		$FrameContent = substr($ID3Header, $CurPos + 10, $FrameLength);
		$CurPos = $CurPos + $FrameLength + 10;

		if(array_key_exists($FrameName, $frames) == true):
			$header[$frames[$FrameName]] = $FrameContent;
		elseif($unuebliche_tags == true):
			if ($FrameName == "APIC")
			{
				// Textenkodierung auslesen (relevant fuer die Beschreibung)
				$textencoding = ord(substr($FrameContent, 0, 1));

				// Trennungszeichen bestimmen
				// 1 Byte fuer Latin1 und UTF8
				// 2 Bytes fuer UTF16 und UTF16BE
				$textdelimiter = chr(0);
				if ($textencoding == 1 || $textencoding == 2)
					$textdelimiter .= chr(0);

				$pos = 1;

				// Mimetype suchen (durch Nullbyte beendet)
				$k = strpos($FrameContent, chr(0), $pos);
				$mimetype = substr($FrameContent, $pos, $k - $pos);
				$pos = $k + 1;

				// Picture type
				$picture_type = ord(substr($FrameContent, $pos, 1));
				$pos++;

				// Beschreibung
				$k = strpos($FrameContent, $textdelimiter, $pos);
				if (strlen($textdelimiter) == 2 && ($k - $pos) % 2 != 0)
					$k++;

				$description = substr($FrameContent, $pos, $k - $pos);
				$pos = $k + strlen($textdelimiter);
				
				// Rest in Datei schreiben
				$filename = 'test.';
				if ($mimetype == 'image/png')
					$filename .= 'png';
				else if ($mimetype == 'image/jpeg')
					$filename .= 'jpg';

				file_put_contents($filename, substr($FrameContent, $pos));
			}

			$counter++;
			$header[$FrameName."_".$counter] = $FrameContent;
		endif;
	else:
		break;
	endif;
endwhile;
```
Das kannst du natürlich frei an deine Ansprüche anpassen. Wenn du z.B. nur das Bild haben willst, dann kannst du auch nur MIME-Type und das Bild auslesen und den Rest auslassen etc.

Deine Längenberechnung stimmte nicht (und war ziemlich umständlich), ich habe das durch folgende Funktion ersetzt:

```
function toNumber($data) {
	$sum = 0;
	$last = strlen($data) - 1;

	for($i = 0; $i <= $last; $i++)
		$sum |= ord($data[$i]) << (($last - $i) * 8);

	return $sum;
}
```

Ich hoffe, dass dich das weiter bringt.

Gruß
Marvin


----------



## Html (8. August 2007)

Vielen Dank für die ausführliche Antwort! Bin leider phpnewbie und muss gestehen das ich den Code nicht so wirklich verstehe.

Den Teil zum Auslesen vom id3v1 Tag kann ich ja noch nachvollziehen aber der Teil mit den Frames ....?.....

Habe jetzt mal folgenden Code getestet. 


```
<?php

/*
//===================================================\\
|                                                     |
|               ID-Tag Reader by Ansi                 |
|                                                     |
|          Reading IDv1.1 and IDv4.0 Tags             |
|                                                     |
|     if you want more frames to be read consult      |
|        http://www.id3.org/id3v2.3.0.html            |
|                                                     |
|                                                     |
\\===================================================//
*/
function toNumber($data) {
    $sum = 0;
    $last = strlen($data) - 1;

    for($i = 0; $i <= $last; $i++)
        $sum |= ord($data[$i]) << (($last - $i) * 8);

    return $sum;
}  



function readHeader($mp3){

// -------> Config <-------

$unuebliche_tags = true;    //gibt auch Tags aus, die von der üblichen Software *nicht* genutzt werden

// -------> Config <-------

    $header = array();

    //Liste der genutzen Frames für ID3v2.3 und deren
    //Rückgabe im $header[]- Array. Um Übersichtlichkeit
    //zu wahren werden alle Frames, die nicht in dieser
    //Liste stehen standardmäßig später *nicht* ausgegeben.
    $frames = array(
        'TPE1'    =>    'artist',
        'TALB'    =>    'album',
        'COMM'    =>    'comment',
        'TBPM'    =>    'bitrate',
        'TCON'    =>    'genre',
        'TCOM'    =>    'composer',
        'TDAT'    =>    'date',
        'TENC'    =>    'encoder',
        'TIT2'    =>    'title',
        'TIT3'    =>    'subtitle',
        'TLAN'    =>    'languages',
        'TLEN'    =>    'length',
        'TYER'    =>    'year',
        'TRCK'    =>    'track',
        'TORY'    =>    'year',
        'TCOP'    =>    'copyright');
    
    //Liste der Genres und ihrer Entsprechung
    $genres = array(
        0   => 'Blues',
        1   => 'Classic Rock',
        2   => 'Country',
        3   => 'Dance',
        4   => 'Disco',
        5   => 'Funk',
        6   => 'Grunge',
        7   => 'Hip-Hop',
        8   => 'Jazz',
        9   => 'Metal',
        10  => 'New Age',
        11  => 'Oldies',
        12  => 'Other',
        13  => 'Pop',
        14  => 'R&B',
        15  => 'Rap',
        16  => 'Reggae',
        17  => 'Rock',
        18  => 'Techno',
        19  => 'Industrial',
        20  => 'Alternative',
        21  => 'Ska',
        22  => 'Death Metal',
        23  => 'Pranks',
        24  => 'Soundtrack',
        25  => 'Euro-Techno',
        26  => 'Ambient',
        27  => 'Trip-Hop',
        28  => 'Vocal',
        29  => 'Jazz+Funk',
        30  => 'Fusion',
        31  => 'Trance',
        32  => 'Classical',
        33  => 'Instrumental',
        34  => 'Acid',
        35  => 'House',
        36  => 'Game',
        37  => 'Sound Clip',
        38  => 'Gospel',
        39  => 'Noise',
        40  => 'Alternative Rock',
        41  => 'Bass',
        42  => 'Soul',
        43  => 'Punk',
        44  => 'Space',
        45  => 'Meditative',
        46  => 'Instrumental Pop',
        47  => 'Instrumental Rock',
        48  => 'Ethnic',
        49  => 'Gothic',
        50  => 'Darkwave',
        51  => 'Techno-Industrial',
        52  => 'Electronic',
        53  => 'Pop-Folk',
        54  => 'Eurodance',
        55  => 'Dream',
        56  => 'Southern Rock',
        57  => 'Comedy',
        58  => 'Cult',
        59  => 'Gangsta',
        60  => 'Top 40',
        61  => 'Christian Rap',
        62  => 'Pop/Funk',
        63  => 'Jungle',
        64  => 'Native US',
        65  => 'Cabaret',
        66  => 'New Wave',
        67  => 'Psychadelic',
        68  => 'Rave',
        69  => 'Showtunes',
        70  => 'Trailer',
        71  => 'Lo-Fi',
        72  => 'Tribal',
        73  => 'Acid Punk',
        74  => 'Acid Jazz',
        75  => 'Polka',
        76  => 'Retro',
        77  => 'Musical',
        78  => 'Rock & Roll',
        79  => 'Hard Rock',
        80  => 'Folk',
        81  => 'Folk-Rock',
        82  => 'National Folk',
        83  => 'Swing',
        84  => 'Fast Fusion',
        85  => 'Bebob',
        86  => 'Latin',
        87  => 'Revival',
        88  => 'Celtic',
        89  => 'Bluegrass',
        90  => 'Avantgarde',
        91  => 'Gothic Rock',
        92  => 'Progressive Rock',
        93  => 'Psychedelic Rock',
        94  => 'Symphonic Rock',
        95  => 'Slow Rock',
        96  => 'Big Band',
        97  => 'Chorus',
        98  => 'Easy Listening',
        99  => 'Acoustic',
        100 => 'Humour',
        101 => 'Speech',
        102 => 'Chanson',
        103 => 'Opera',
        104 => 'Chamber Music',
        105 => 'Sonata',
        106 => 'Symphony',
        107 => 'Booty Bass',
        108 => 'Primus',
        109 => 'Porn Groove',
        110 => 'Satire',
        111 => 'Slow Jam',
        112 => 'Club',
        113 => 'Tango',
        114 => 'Samba',
        115 => 'Folklore',
        116 => 'Ballad',
        117 => 'Power Ballad',
        118 => 'Rhytmic Soul',
        119 => 'Freestyle',
        120 => 'Duet',
        121 => 'Punk Rock',
        122 => 'Drum Solo',
        123 => 'Acapella',
        124 => 'Euro-House',
        125 => 'Dance Hall',
        126 => 'Goa',
        127 => 'Drum & Bass',
        128 => 'Club-House',
        129 => 'Hardcore',
        130 => 'Terror',
        131 => 'Indie',
        132 => 'BritPop',
        133 => 'Negerpunk',
        134 => 'Polsk Punk',
        135 => 'Beat',
        136 => 'Christian Gangsta Rap',
        137 => 'Heavy Metal',
        138 => 'Black Metal',
        139 => 'Crossover',
        140 => 'Contemporary Christian',
        141 => 'Christian Rock',
        142 => 'Merengue',
        143 => 'Salsa',
        144 => 'Trash Metal',
        145 => 'Anime',
        146 => 'Jpop',
        147 => 'Synthpop');

    //MP3 einlesen
    $filelength = filesize($mp3);
    $hdl = fopen($mp3, "rb");
    if($hdl):
            $content .= fread($hdl, $filelength);
        fclose($hdl);
    endif;

    //Versuche ID3v2 Tag zu finden
    $IDTag = strpos($content, "ID3");

    //Wenn ID3v2 nicht gefunden wird nach ID3v1
    //suchen, entspricht TAG
    if($IDTag === false):
        $IDTag = strpos($content, "TAG");        
        if($IDTag === false):
            $header['type'] = "kein Header gefunden";
            return $header;
            exit;
        else:
            $header['type'] = "ID3v1";
            //$header['pos'] = $IDTag;
        endif;
    else:
        $header['type'] = "ID3v2";
        //$header['pos'] = $IDTag;
    endif;

    switch ($header['type']):
        //ID3v2 Tags auslesen: Flexiblere Felder
        case "ID3v2":
            //10 Byte des ID3 Headers auslesen
            $ID3Header = substr($content, $IDTag, 10);
            
            //Exakte Version der genutzen ID3v2 Version ermitteln
            $header['type'] .= ".".ord(substr($ID3Header, 3, 1)).".".ord(substr($ID3Header, 4, 1));
            
            //Gesamte Headerlänge inklusive Frames ermitteln
            $HeaderLength =  265 * 8 * 8 * ord(substr($ID3Header, 6, 1)) + 265 * 8 * ord(substr($ID3Header, 7, 1)) + 265 *  ord(substr($ID3Header,  8, 1)) + ord(substr($ID3Header,  9, 1));
            
            //ID3 Header in Variabel ablegen, $content leeren
            $ID3Header = substr($content, $IDTrag, $HeaderLength + 10);
            $content = "";

            //Startposition für Frameerkennung ist Byte 10
            $CurPos = 10;
            
            //So lange Frames suchen / auslesen, bis eine Frameposition
            //leer ist oder der Header durchlaufen wurde
while($CurPos < $HeaderLength + 10):
    $FrameName = substr($ID3Header, $CurPos, 4);
    if(trim($FrameName) != ""):
        $FrameLength = toNumber(substr($ID3Header, $CurPos+4, 4));
        $FrameContent = substr($ID3Header, $CurPos + 10, $FrameLength);
        $CurPos = $CurPos + $FrameLength + 10;

        if(array_key_exists($FrameName, $frames) == true):
            $header[$frames[$FrameName]] = $FrameContent;
        elseif($unuebliche_tags == true):
            if ($FrameName == "APIC")
            {
                // Textenkodierung auslesen (relevant fuer die Beschreibung)
                $textencoding = ord(substr($FrameContent, 0, 1));

                // Trennungszeichen bestimmen
                // 1 Byte fuer Latin1 und UTF8
                // 2 Bytes fuer UTF16 und UTF16BE
                $textdelimiter = chr(0);
                if ($textencoding == 1 || $textencoding == 2)
                    $textdelimiter .= chr(0);

                $pos = 1;

                // Mimetype suchen (durch Nullbyte beendet)
                $k = strpos($FrameContent, chr(0), $pos);
                $mimetype = substr($FrameContent, $pos, $k - $pos);
                $pos = $k + 1;

                // Picture type
                $picture_type = ord(substr($FrameContent, $pos, 1));
                $pos++;

                // Beschreibung
                $k = strpos($FrameContent, $textdelimiter, $pos);
                if (strlen($textdelimiter) == 2 && ($k - $pos) % 2 != 0)
                    $k++;

                $description = substr($FrameContent, $pos, $k - $pos);
                $pos = $k + strlen($textdelimiter);
                
                // Rest in Datei schreiben
                $filename = 'test.';
                if ($mimetype == 'image/png')
                    $filename .= 'png';
                else if ($mimetype == 'image/jpeg')
                    $filename .= 'jpg';

                file_put_contents($filename, substr($FrameContent, $pos));
            }

            $counter++;
            $header[$FrameName."_".$counter] = $FrameContent;
        endif;
    else:
        break;
    endif;
endwhile;  

        //Hier wird der ID3V1.1 Standard vorausgesetzt,
        //der im letzten Byte des Kommentares (127) den Track speichert
        case "ID3v1.1":
            $header['titel'] = trim(substr($content, $IDTag + 3, 30));
            $header['artist'] = trim(substr($content, $IDTag + 33, 30));
            $header['album'] = trim(substr($content, $IDTag + 63, 30));
            $header['year'] = trim(substr($content, $IDTag + 93, 4));
            $header['comment'] = trim(substr($content, $IDTag + 97, 29));
            $header['track'] = ord(trim(substr($content, $IDTag + 126, 1)));
            $header['genre'] = ord(trim(substr($content, $IDTag + 127, 1)));
            if(!$header['genre'] === false):
                $header['genre'] = $genres[$header['genre']];
            endif;
            break;
    endswitch;

    return $header;

}



$ausgabe=readHeader('LeAnn Rimes\2004 The Best Of\04 - LeAnn Rimes - I Need You.mp3');


print_r($ausgabe);




?>
```






Doch bei der Ausgabe des arrays steht in APIC_1 wieder nur Zeichen:

Aber denke eher das ich den Code nicht ganz richtig zusammen gebastelt hab!?




Sorry das ich als nachfragen muss, mfg Olli



Halt, jetzt habe ich festgestellt das ein Bild test.jpg in dem Ordner erstellt wurde. Allerdings ist das Bild nur zur Hälfte auufgebaut. 
Sorry wenn ich jetzt frage aber warum erstellt er mir ein neues Bild.... Möchte doch nur den Pfad zum OriginalCover in einer Variable gespeichert haben.


Mensch das ist echt kompliziert...


----------



## Marvin Schmidt (8. August 2007)

Moin,



Html hat gesagt.:


> Halt, jetzt habe ich festgestellt das ein Bild test.jpg in dem Ordner erstellt wurde. Allerdings ist das Bild nur zur Hälfte auufgebaut.
> Sorry wenn ich jetzt frage aber warum erstellt er mir ein neues Bild.... Möchte doch nur den Pfad zum OriginalCover in einer Variable gespeichert haben.



Es gibt in dem Sinne kein "Original-Cover". Das Cover wird mit Hilfe der 'Attached picture'-Frame des ID3-Tags in die MPEG-Datei eingebettet. Sprich du musst es erst auslesen und speichern. Danach kannst du den Pfad zum gespeicherten Bild anstelle des Frame-Inhaltes zum Array hinzufügen.

Dass es nur zur Hälfte ausgelesen wird, liegt an folgender Zeile:

```
$HeaderLength =  265 * 8 * 8 * ord(substr($ID3Header, 6, 1)) + 265 * 8 * ord(substr($ID3Header, 7, 1)) + 265 *  ord(substr($ID3Header,  8, 1)) + ord(substr($ID3Header,  9, 1));
```
Die Berechnung stimmt nicht, weswegen er vorzeitig aufhört den Tag auszulesen.
Hierfür kannst du wieder die toNumber-Funktion nutzen:

```
$HeaderLength =  toNumber(substr($ID3Header, 6, 4));
```

Gruß
Marvin


----------



## arubbert (9. November 2007)

Moin zusammen,

da ich meine Tags im Format ID3v2.3.0 abspeichere, hab ich leider mit dem Code probleme. Kann mir jemand behilflich sein, daß ich auch diese Tags auslesen kann?

Danke und Grüße
Andreas


----------



## Laudine (21. September 2008)

Hallo...
... ich habe mit diesem Script gute Erfahrung gemacht, möchte aber gerne eine kleine Ergänzung beitragen.

Ich musste feststellen, dass Leerzeichen wenn ich mir das array auf einer html-Seite habe anzeigen lassen, nicht dargestellt wurden.
Das lag daran, dass die Null-Bytes zwischen den einzelnen Zeichen noch in den Strings waren und bei der Seitendarstellung Probleme verursachen.

Abhilfe: Einfach diese Null-Bytes entfernen:


```
$songInfo = readHeader($mp3);
foreach($songInfo as $key => $value) { $songInfo[$key] = str_replace(chr(00),'', $value); }
```


----------



## lfox (12. Januar 2010)

Vielen Dank für die Beiträge. Hat jetzt prima funktioniert. War schon lange auf der Suche nach Beiträgen zu diesem Thema.


----------

