ID3 Tags - Bilder?

09/26/2010 16:39 mipez#1
Code:
#include-once
;===============================================================================
;
; Description:      Retrieve MP3 tag info
; Parameter(s):     File name
; Requirement(s):   Autoit Beta 3.104.123+ (DllStruct, Binary, RegExp)
; Return Value(s):  On Success - array with data:
;                       0 - Title
;                       1 - Artist
;                       2 - Album
;                       3 - Year
;                       4 - Comment
;                       5 - Track number
;                       6 - Genre
;                       7 - Length (for V2 only, may be used for determine length for VBR files)
;                       8 - Tag version
;                   On Failure empty string and sets @ERROR:
;                       1 - Info not found
; Author(s):        YDY (Lazycat)
; Version:          2.5.1
; Date:             06.05.2006
; Note(s):          None
;
;===============================================================================

Func _MP3GetTag($sFile)
    Local $ret = DllCall("kernel32.dll","int","CreateFile", _
                        "str",$sFile, _
                        "int",0x80000000, _
                        "int",0, _
                        "ptr",0, _
                        "int",3, _
                        "int",0x80, _
                        "ptr",0)
                        
    If @error OR Not $ret[0] Then 
        SetError(1)
        Return ""
    Endif
    Local $vTag = _MP3GetV2Tag($ret[0])
    If not IsArray($vTag) Then $vTag = _MP3GetV1Tag($ret[0], FileGetSize($sFile) - 128)
    DllCall("kernel32.dll","int","CloseHandle","int", $ret[0])
    If IsArray($vTag) Then Return $vTag
    SetError(1)
    Return ""
EndFunc

Func _MP3GetV1Tag($hFile, $nTagPos)
    Local $pTag = _FileReadToStruct("char[3];char[30];char[30];char[30];char[4];char[30];byte", $hFile, $nTagPos)    
    If not (_DllStructArrayAsString($pTag, 1, 3) == "TAG") Then Return "" ; ID3v1 tag NOT found
    Local $asInfo[9]
    $asInfo[0] = _DllStructArrayAsString($pTag, 2, 30) ; Title
    $asInfo[1] = _DllStructArrayAsString($pTag, 3, 30) ; Peformer
    $asInfo[2] = _DllStructArrayAsString($pTag, 4, 30) ; Album
    $asInfo[3] = _DllStructArrayAsString($pTag, 5, 4)  ; Year
    $asInfo[4] = _DllStructArrayAsString($pTag, 6, 30) ; Comment
    $asInfo[5] = ""                                    ; Track number  
    $asInfo[8] = "1.0"
    If DllStructGetData($pTag, 6, 29) = 0 Then ; Version 1.1
        $asInfo[5] = DllStructGetData($pTag, 6, 30) 
        $asInfo[8] = "1.1"
    EndIf
    $asInfo[6] = _MP3GetGenreByID(DllStructGetData($pTag, 7)) ; Genre
    $asInfo[7] = ""
    Return($asInfo)
EndFunc

Func _MP3GetV2Tag($hFile)
Local $nTagSize = 1, $nFrameOffset = 10, $nTagInfo
Local $asInfo[9], $sID, $pFrame, $pHeader, $pTemp, $nFrameSize, $sData

$pHeader = _FileReadToStruct("char[3];byte;byte;byte;dword;dword", $hFile, 0)
If not (StringLeft(DllStructGetData($pHeader, 1), 3) = "ID3") Then Return

For $i = 0 To 8
    $asInfo[$i] = ""
Next

; Set tag version
$asInfo[8] = StringFormat("2.%d.%d", DllStructGetData($pHeader, 2), DllStructGetData($pHeader, 3))

$nTagInfo = DllStructGetData($pHeader, 4)
$nTagSize = _SSIntToInt(DllStructGetData($pHeader, 5)) + 10

If _IsBitSet($nTagInfo, 4) Then $nTagSize = $nTagSize + 10  ; Footer presented, RARE case
If _IsBitSet($nTagInfo, 6) Then $nFrameOffset = 10 + _SSIntToInt(DllStructGetData($p, 5)) ; ExtHeader presented

$pFrame = DllStructCreate("char[4];dword;short")

While $nFrameOffset < $nTagSize
    $pFrame = _FileReadToStruct($pFrame, $hFile, $nFrameOffset)
    $sID = DllStructGetData($pFrame, 1)
    $nFrameSize = _SSIntToInt(DllStructGetData($pFrame, 2))
    If $nFrameSize > $nTagSize Then Exitloop
    $pTemp = _FileReadToStruct("byte;byte[" & $nFrameSize - 1 & "]", $hFile, $nFrameOffset + 10)

    $sData = _GetDecodedText(DllStructGetData($pTemp, 1), DllStructGetData($pTemp, 2), $nFrameSize)

    Select
        Case $sID == "TIT2" ; Title 
            $asInfo[0] = $sData
        Case $sID == "TPE1" ; Performer (primary)
            $asInfo[1] = $sData
        Case $sID == "TPE2" ; Performer (secondary)
            If $asInfo[1] == "" Then $asInfo[1] = $sData
        Case $sID == "TALB" ; Album
            $asInfo[2] = $sData
        Case $sID == "TYER" ; Year
            $asInfo[3] = $sData
        Case $sID == "COMM" ; Required special
            If $asInfo[4] == "" Then ; MS ShellExt tag editor add empty COMM at the end of tag...
                ; Not sure if this is correct method...
                If DllStructGetData($pTemp, 2, 4) = 0 Then
                   If DllStructGetData($pTemp, 2, 5) = 0 Then
                       $asInfo[4] = _GetDecodedText(DllStructGetData($pTemp, 1), StringTrimLeft(DllStructGetData($pTemp, 2), 5), $nFrameSize)
                   Else
                       $asInfo[4] = _GetDecodedText(DllStructGetData($pTemp, 1), StringTrimLeft(DllStructGetData($pTemp, 2), 4), $nFrameSize)
                   EndIf
                EndIf
            EndIf
        Case $sID == "TRCK" ; Track number
            $asInfo[5] = $sData
        Case $sID == "TCON" ; Genre
            $asInfo[6] = $sData
            $sData = StringRegExp($sData, "\((\d{1,3})\)", 1)
            If @extended Then $asInfo[6] = _MP3GetGenreByID($sData[0])
        Case $sID == "TLEN" ; Length!
            $asInfo[7] = Int(Number($sData)/1000)
            $asInfo[7] = StringFormat("%d:%02d", Int($asInfo[7] / 60), $asInfo[7] - Int($asInfo[7] / 60) * 60)
        Case StringLen($sID) < 4 or not StringIsAlNum($sID)
            Exitloop ; Zero-byte padding (end of tag), wrong sID -> Exitloop                           
    EndSelect
    $nFrameOffset = $nFrameOffset + $nFrameSize + 10  
    $pTemp = 0
Wend
Return($asInfo)
EndFunc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Support functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _MP3GetGenreByID($iID)
    Local $asGenre = StringSplit("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,Heavy Metal,Black Metal," & _
    "Crossover,Contemporary C,Christian Rock,Merengue,Salsa,Thrash Metal,Anime,JPop,SynthPop", ",")
    If ($iID >= 0) and ($iID < 148) Then Return $asGenre[$iID + 1]
    Return("")
EndFunc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Common functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _DllStructArrayAsString($p, $index, $size, $start = 1)
    Local $sTemp = "", $char
    For $i = $start to $size
        $char = DllStructGetData($p, $index, $i)
        If $char = 0 then Return $sTemp
        $sTemp &= Chr($char)
    Next
    Return $sTemp
EndFunc

Func _SSIntToInt($nSSInt)
    Local $nRet = 0
    For $i = 0 To 3
        $nRet = $nRet + BitShift(BitAND(BitShift($nSSInt, 8 * (3-$i)), 0xFF), -7 * $i)
    Next
    Return($nRet)
EndFunc

Func _IsBitSet($nNum, $nBit)
    Return BitAND(BitShift($nNum, $nBit), 1)
EndFunc

Func _FileReadToStruct($vStruct, $hFile, $nOffset)
    If not DllStructGetSize($vStruct) Then $vStruct = DllStructCreate($vStruct)
    Local $nLen    = DllStructGetSize($vStruct)
    Local $ret    = DllCall("kernel32.dll","int","SetFilePointer", _
                    "int",$hFile, _
                    "int",$nOffset, _
                    "int",0, _
                    "int",0) ; FILE_BEGIN
    Local $pRead = DllStructCreate("dword")
    $ret    = DllCall("kernel32.dll","int","ReadFile", _
                    "int",$hFile, _
                    "ptr",DllStructGetPtr($vStruct), _
                    "int", $nLen, _
                    "ptr",DllStructGetPtr($pRead), _
                    "ptr",0)
    If @error Then 
        SetError(1)
    EndIf
    Local $nRead = DllStructGetData($pRead, 1)
    $pRead = 0
    SetExtended($nRead)
    If not ($nRead = $nLen) Then SetError(2)
    Return $vStruct
EndFunc

Func _GetDecodedText($nType, $sText, $nSize)
    Switch $nType
        Case 0        ; ASCII
            If ($nSize - 1) = 1 Then Return Chr($sText)
            Return $sText
        Case 0x1, 0x2 ; Unicode
            Return _Unicode2Asc($sText)
        Case 0x3      ; UTF-8
            Return _Unicode2Asc(_Utf82Unicode($sText))
    EndSwitch
EndFunc

;;;;;;;;;;;;;;;;;;;;;;;; Unicode conversion functions originally written by Arilvv ;;;;;;;;;;;;;;;;;;;;;;;

Func _Unicode2Asc($UniString)
    Local $lb, $hb, $InpBuf
    Local $BufferLen = StringLen($UniString)
    Local $Input = DllStructCreate("ubyte[" & $BufferLen & "]")
    DllStructSetData($Input, 1, $UniString)
    $lb = DllStructGetData($Input, 1, 1)
    $hb = DllStructGetData($Input, 1, 2)
    If (($lb = 0xFF) and ($hb = 0xFE)) or (($lb = 0xFE) and ($hb = 0xFF)) Then ; BOM found
        $BufferLen -= 2
        $InpBuf = DllStructCreate("byte[" & $BufferLen & "]", DllStructGetPtr($Input) + 2)
    Else
        $InpBuf = DllStructCreate("byte[" & $BufferLen & "]", DllStructGetPtr($Input))
    EndIf

    Local $Output = DllStructCreate("char[" &  $BufferLen & "]")    
    Local $Return = DllCall("kernel32.dll", "int", "WideCharToMultiByte", _
        "int", 0, _
        "int", 0, _
        "ptr", DllStructGetPtr($InpBuf), _
        "int", $BufferLen / 2, _
        "ptr", DllStructGetPtr($Output), _
        "int", $BufferLen, _
        "int", 0, _
        "int", 0)   
    Return DllStructGetData($Output, 1)
EndFunc

Func _Utf82Unicode($Utf8String)
    Local $BufferSize = StringLen($Utf8String) * 2
    Local $Buffer = DllStructCreate("byte[" & $BufferSize & "]")
    Local $Return = DllCall("Kernel32.dll", "int", "MultiByteToWideChar", _
        "int", 65001, _
        "int", 0, _
        "str", $Utf8String, _
        "int", StringLen($Utf8String), _
        "ptr", DllStructGetPtr($Buffer), _
        "int", $BufferSize)
    Return StringLeft(DllStructGetData($Buffer, 1), $Return[0] * 2)
EndFunc

;===============================================================================
;
; Description:      Retrieve MP3 (MP2, MPA) basic information
; Parameter(s):     File name
; Requirement(s):   None
; Return Value(s):  On Success - array with data:
;                       0 - MPEG version
;                       1 - Layer
;                       2 - Bitrate
;                       3 - Frequency
;                       4 - Channel Mode
;                       5 - Duration
;                       6 - Frames
;                       7 - CRC protected
;                       8 - Copyrighted
;                       9 - Original
;                   On Failure empty string and sets @ERROR:
;                       1 - Info not found
; Author(s):        YDY (Lazycat) <[Only registered and activated users can see links. Click Here To Register...]>
; Version:          1.2.00
; Date:             30.12.2004
; Note(s):          None
;
;===============================================================================
Func _GetMP3Info($file)
Local $data[10], $offset = 1, $isValid = 0, $isVBR = 0
Local $aVersion = StringSplit("MPEG 2.5|Undefined|MPEG 2.5|MPEG 1", "|")
Local $aLayer = StringSplit("Undefined|Layer III|Layer II|Layer I", "|")
Local $sBitrate = ""
Local $sFrequency = ""
Local $aChanMode = StringSplit("Stereo|Joint stereo|Dual channel|Mono", "|")
Local $aFlags = StringSplit("No|Yes", "|")
Local $head, $nVer, $nLay

If _FileReadAtOffsetHEX ($file, 1, 3) = "494433" Then ; ID3v2 tag found
    $offset = _HEXToSSInt(_FileReadAtOffsetHEX ($file, 7, 4)) + 10 ; End of ID3v2 tag
Endif

For $ic = $offset to 4096 + $offset
    $marker = _FileReadAtOffsetHEX ($file, $ic, 2)
    $marker = StringLeft($marker, 3)
    If StringInStr("FFF,FFE", $marker) Then ; Assume that is frame start
        $head = _HexToBin(_FileReadAtOffsetHEX ($file, $ic, 4))
        $nVer = _GetRBits($head, 19, 2)
        $nLay = _GetRBits($head, 17, 2)
        If ($nVer <> 1) and ($nLay <> 0) Then
            If _FileReadAtOffsetHEX ($file, $ic+36, 4) = "58696E67" Then $isVBR = 1 ; Is this a right way?..
            $isValid = 1
            Exitloop
        Endif
    Endif
Next

If not $isValid Then 
    SetError(1) ; Frame not found (not mp3 data?)
    Return ("")
Endif

Select
    Case $nVer = 3
        $sFrequency = "44100|48000|32000|Undefined"
    Case $nVer = 2
        $sFrequency = "22050|24000|16000|Undefined"
    Case $nVer = 0
        $sFrequency = "11025|12000|8000|Undefined"
EndSelect
Local $aFrequency = StringSplit($sFrequency, "|")
$data[3] = _GetData($aFrequency, _GetRBits($head, 10, 2))

Local $pad = 0, $bitrate, $framesize, $frames, $length, $fps
If _GetRBits($head, 9, 1) Then $pad = 1

If $isVBR Then
    $data[2] = "Variable"
Else
    Select
        Case $nVer = 3 and $nLay = 3
            $sBitrate = "Free|32|64|96|128|160|192|224|256|288|320|352|384|416|448|Undefined"
        Case $nVer = 3 and $nLay = 2
            $sBitrate = "Free|32|48|56|64|80|96|112|128|160|192|224|256|320|384|Undefined"
        Case $nVer = 3 and $nLay = 1
            $sBitrate = "Free|32|40|48|56|64|80|96|112|128|160|192|224|256|320|Undefined"
        Case $nVer = 2 and $nLay = 3
            $sBitrate = "Free|32|48|56|64|80|96|112|128|144|160|176|192|224|256|Undefined"
        Case ($nVer = 2 and $nLay = 2) or ($nVer = 2 and $nLay = 1)
            $sBitrate = "Free|8|16|24|32|40|48|56|64|80|96|112|128|144|160|Undefined"
    EndSelect
    Local $aBitrate = StringSplit($sBitrate, "|")
    $data[2] = _GetData($aBitrate, _GetRBits($head, 12, 4))
    $bitrate = 1000 * $data[2]
    If $nLay = 3 Then 
        $framesize = (((12 * $bitrate) / $data[3]) + $pad) * 4
        $fps = $data[3]/384
    Else
        $framesize = ((144 * $bitrate) / $data[3]) + $pad
        $fps = $data[3]/1152
    Endif
    $frames = FileGetSize($file) / $framesize
    $length = $frames / $fps
Endif

$data[0] = _GetData($aVersion, $nVer)
$data[1] = _GetData($aLayer, $nLay)
$data[4] = _GetData($aChanMode, _GetRBits($head, 6, 2))
$data[5] = StringFormat("%d:%02d", Int($length / 60), $length - Int($length / 60) * 60)
$data[6] = Int($frames)
$data[7] = _GetData($aFlags, not _GetRBits($head, 16, 1)) ; CRC
$data[8] = _GetData($aFlags, _GetRBits($head, 3, 1)) ; Private
$data[9] = _GetData($aFlags, _GetRBits($head, 2, 1)) ; Original
Return($data) 
EndFunc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Support functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _GetRBits($str, $pos, $size)
    Local $ic, $res = 0, $bStr = StringMid($str, 33 - $pos - $size, $size)
    For $ic = 0 to $size-1
        If StringMid($bStr, $size-$ic, 1) == "1" Then $res = $res + 2^$ic
    Next
    Return ($res)
EndFunc

Func _GetData(ByRef $array, $val)
    If $val > UBound($array)-1 Then Return("Undefined")
    Return ($array[$val+1])
EndFunc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Common functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Func _HEXToSSInt($sHexStr)
    Local $iCnt, $iLen, $sTempStr = "", $iReturn = 0
    For $iCnt = 1 to StringLen($sHexStr) Step 2
        $sTempStr = $sTempStr & StringTrimLeft(_HexToBin(StringMid($sHexStr, $iCnt, 2)), 1)
    Next
    $iLen = StringLen($sTempStr)
    For $iCnt = 0 To $iLen - 1
        $iReturn = $iReturn + Number(StringMid($sTempStr, $iLen - $iCnt, 1)) * 2^$iCnt
    Next
    Return($iReturn)
EndFunc

Func _HexToBin($str)
Local $res="", $i
While StringLen($str) > 0
    $val = Dec(StringRight($str, 1))
    $str = StringTrimRight($str, 1)
    For $i = 1 to 4
        $res = String(Mod($val, 2)) & $res
        $val = Int($val/2)
    Next
Wend
Return ($res)
EndFunc

Func _FileReadAtOffsetHEX ($file, $offset, $bytes)
    Local $tfile = FileOpen($file, 0)
    Local $tstr = "", $i
    FileRead($tfile, $offset-1)
    For $i = $offset To $offset + $bytes - 1
        $tstr =  $tstr & Hex(Asc(FileRead($tfile, 1)), 2)
    Next
    FileClose($tfile)
    Return ($tstr)
Endfunc
Hab 2 UDFs von Lazycat zusammengemixt. Alles schön und gut, aber was ist mit den Bildern? xD
Hab in der kernel32.dll nix gefunden :x
09/26/2010 18:20 PenGuin :O#2
Achja, warum nimmst du fürs auslesen nicht die BassTags.dll mit der dazugehörigen UDF? :D
09/26/2010 18:30 mipez#3
kA, arbeite mit der hier schon ne Weile und wirkliche Unterschiede gibts nich xD
09/26/2010 20:00 PenGuin :O#4
Performance und Geschwindigkeit? :)
09/26/2010 20:31 mipez#5
Quote:
Originally Posted by pinguin94 View Post
Performance und Geschwindigkeit? :)
Beim einmaligen Auslesen? Ich glaubs auch xD
09/26/2010 22:03 PenGuin :O#6
Quote:
Originally Posted by mipez View Post
Beim einmaligen Auslesen? Ich glaubs auch xD
Jede ms zählt! :D
09/26/2010 22:22 mipez#7
ns ;D

Aber eine wirkliche Antwort kannste mir nich liefern? q_q
09/27/2010 16:16 PenGuin :O#8
Ne momentan nicht, da müsste ich mich länger in den Code einarbeite, hab ich nur grad keine Lust zu, weil ich nen Programm bis Freitag für ne Firma gemacht haben muss ;)
09/27/2010 17:01 mipez#9
Ok, anders: In welcher Windoof oder WMP dll sind die Befehle gespeichert? ;D
09/27/2010 18:06 PenGuin :O#10
Welche Befehle?
09/27/2010 18:22 mipez#11
Quote:
Originally Posted by pinguin94 View Post
Welche Befehle?
Auslese der ID3 Daten^^
09/27/2010 18:38 PenGuin :O#12
Von Bildern?
09/27/2010 20:22 | Moep |#13
Hier die UDF:
[Only registered and activated users can see links. Click Here To Register...]

Die ID "APIC Attached picture" hält das Bild.
09/27/2010 20:37 mipez#14
Öhm ja, schon probiert, und die wirft kein Bild aus ;D
Array bleibt an der Stelle schlicht leer... egal ob spezifisch oder gesamte IDs...
Und SetTagField funzt auch nich ;D
09/27/2010 21:34 PenGuin :O#15
Ach jetzt check ich deine Frage :D
Ich schau mal was ich so hab :)