#include-once #include #include #include #cs #ID3.au3 User-Defined Functions# ;======================================================================= UDF Description ..............: Reads/Writes ID3v1.1 ID3v2.3, ID3v2.4 and APEv2 Tags in MP3 files UDF AutoIt Forum Link.........: http://www.autoitscript.com/forum/index.php?showtopic=43950&st=0 UDF AutoIt Version Required...: 23rd December, 2011 - v3.3.8.0 of AutoIt FUNCTIONS LIST ...............: MAIN UDF FUNCTION LIST _ID3ReadTag() _ID3GetTagField() _ID3SetTagField() _ID3WriteTag() _ID3RemoveTag() _ID3CreateTag() _ID3DeleteFiles() ID3v1 EXTENDED FUNCTION LIST _ID3v1Tag_ReadFromFile() _ID3v1Tag_GetVersion() _ID3v1Field_GetString() _ID3v1Field_SetString() _ID3v1Tag_WriteToFile() ID3v1 HELPER FUNCTION LIST _h_ID3v1_GetGenreFromID() _h_ID3v1_GetGenreID() ID3v2 EXTENDED FUNCTION LIST _ID3v2Tag_ReadFromFile() _ID3v2Tag_WriteToFile() _ID3v2Tag_RemoveFrame() _ID3v2Tag_GetVersion() _ID3v2Tag_GetHeaderFlags() _ID3v2Tag_GetTagSize() _ID3v2Tag_GetExtendedHeader() _ID3v2Tag_GetFooter() _ID3v2Tag_GetZPAD() _ID3v2Frame_GetBinary() _ID3v2Frame_SetBinary() _ID3v2Frame_GetFields() _ID3v2Frame_SetFields() ID3v2 HELPER FUNCTION LIST _h_ID3v2Tag_EnumerateFrameIDs() _h_ID3v2FrameHeader_GetFrameID() _h_ID3v2FrameHeader_GetFrameSize() _h_ID3v2FrameHeader_GetFlags() _h_ID3v2_GetFrameT000_TZZZ() _h_ID3v2_CreateFrameT000_TZZZ() _h_ID3v2_GetFrameTXXX() _h_ID3v2_CreateFrameTXXX() _h_ID3v2_GetFrameW000_WZZZ() _h_ID3v2_CreateFrameW000_WZZZ() _h_ID3v2_GetFrameWXXX() _h_ID3v2_CreateFrameWXXX() _h_ID3v2_GetFrameCOMM() _h_ID3v2_CreateFrameCOMM() _h_ID3v2_GetFrameAPIC() _h_ID3v2_CreateFrameAPIC() _h_ID3v2_GetFrameUSLT() _h_ID3v2_CreateFrameUSLT() _h_ID3v2_GetFramePCNT() _h_ID3v2_CreateFramePCNT() _h_ID3v2_GetFrameUFID() _h_ID3v2_CreateFrameUFID() _h_ID3v2_GetFramePOPM() _h_ID3v2_CreateFramePOPM() _h_ID3v2_GetFramePRIV() _h_ID3v2_CreateFramePRIV() _h_ID3v2_GetFrameRGAD() _h_ID3v2_DecodeTextToString() _h_ID3v2_EncodeStringToBinary() _h_ID3v2_ConvertFrameID() MPEG FUNCTION LIST _MPEG_GetFrameHeader() _MPEG_IsValidHeader() _MPEG_GetVersion() _MPEG_GetLayer() _MPEG_GetBitRate() _MPEG_GetSampleRate() _MPEG_GetChannelMode() _MPEG_GetChannelModeEx() APEv2 FUNCTION LIST _APEv2Tag_ReadFromFile() _APEv2Tag_GetTagSize() _APEv2_GetItemKeys() _APEv2_GetItemValueBinary() _APEv2_GetItemValueString() _APEv2_RemoveTag() #ce ;======================================================================================================== #cs #ID3.au3 ID3v2 FrameID Definitions# ;==================================================================== FrameIDs as of ID3v2.3,ID3v2.2 (not all may work in UDF) AENC,CRA Audio encryption APIC,PIC Attached picture COMM,COM Comments COMR Commercial frame. CRM Encrypted meta frame (ID3v2.2 only) ENCR Encryption method registration EQUA,EQU Equalization ETCO,ETC Event timing codes GEOB,GEO General encapsulated object GRID Group identification registration IPLS,IPL Involved people list LINK,LNK Linked information MCDI,MCI Music CD identifier MLLT,MLL MPEG location lookup table OWNE Ownership frame PRIV Private frame PCNT,CNT Play counter POPM,POP Popularimeter POSS Position synchronisation frame RBUF,BUF Recommended buffer size RVAD,RVA Relative volume adjustment RVRB,REV Reverb SYLT,SLT Synchronized lyric/text SYTC,STC Synchronized tempo codes TALB,TAL Album/Movie/Show title TBPM,TBP BPM (beats per minute) TCOM,TCM Composer TCON,TCO Content type TCOP,TCR Copyright message TDAT,TDA Date TDLY,TDY Playlist delay TENC,TEN Encoded by TEXT,TXT Lyricist/Text writer TFLT,TFT File type TIME,TIM Time TIT1,TT1 Content group description TIT2,TT2 Title/songname/content description TIT3,TT3 Subtitle/Description refinement TKEY,TKE Initial key TLAN,TLA Language(s) TLEN,TLE Length TMED,TMT Media type TOAL,TOT Original album/movie/show title TOFN,TOF Original filename TOLY,TOL Original lyricist(s)/text writer(s) TOPE,TOA Original artist(s)/performer(s) TORY,TOR Original release year TOWN File owner/licensee TPE1,TP1 Lead performer(s)/Soloist(s) TPE2,TP2 Band/orchestra/accompaniment TPE3,TP3 Conductor/performer refinement TPE4,TP4 Interpreted, remixed, or otherwise modified by TPOS,TPA Part of a set TPUB,TPB Publisher TRCK,TRK Track number/Position in set TRDA,TRD Recording dates TRSN Internet radio station name TRSO Internet radio station owner TSIZ,TSI Size TSRC,TRC ISRC - International Standard Recording Code TSSE,TSS Software/Hardware and settings used for encoding TYER,TYE Year TXXX,TXX User defined text information frame UFID,UFI Unique file identifier USER Terms of use USLT,ULT Unsychronized lyric/text transcription WCOM,WCM Commercial information WCOP,WCP Copyright/Legal information WOAF,WAF Official audio file webpage WOAR,WAR Official artist/performer webpage WOAS,WAS Official audio source webpage WORS Official internet radio station homepage WPAY Payment WPUB,WPB Publishers official webpage WXXX,WXX User defined URL link frame Unofficial Frames Seen in the Wild: These are frames that are not in the specs that programs are writing: http://www.id3.org/Developer_Information COMM w. desc="iTunNORM": iTunes Normalization settings RGAD Replay Gain Adjustment (This is not widely supported and I think it has been superseded by RVA2 in ID3v2.4 (and the XRVA tag for 2.3 compatibility).) TCMP iTunes Compilation Flag TSO2 iTunes uses this for Album Artist sort order TSOC iTunes uses this for Composer sort order XRVA Experimental RVA2 Deprecated ID3v2 frames (Not in ID3v2.4) EQUA Equalization (This frame is replaced by the EQU2 frame) IPLS Involved people list (This frame is replaced by the two frames TMCL, 'Musician credits list', and TIPL, 'Involved people list') RVAD Relative volume adjustment (This frame is replaced by the RVA2 frame, 'Relative volume adjustment (2)') TDAT Date (This frame is replaced by the TDRC frame, 'Recording time') TIME Time (This frame is replaced by the TDRC frame, 'Recording time') TORY Original release year (This frame is replaced by the TDOR frame, 'Original release time') TRDA Recording dates (This frame is replaced by the TDRC frame, 'Recording time') TSIZ Size (The information contained in this frame is in the general case either trivial to calculate for the player or impossible for the tagger to calculate. There is however no good use for such information. The frame is therefore completely deprecated. TYER Year (This frame is replaced by the TDRC frame, 'Recording time') New frames in ID3v2.4 ASPI Audio seek point index EQU2 Equalisation (2) RVA2 Relative volume adjustment (2) SEEK Seek frame SIGN Signature frame TDEN Encoding time TDOR Original release time TDRC Recording time TDRL Release time TDTG Tagging time TIPL Involved people list TMCL Musician credits list TMOO Mood TPRO Produced notice TSOA Album sort order TSOP Performer sort order TSOT Title sort order TSST Set subtitle #ce ;======================================================================================================== #cs #ID3.au3 UDF Latest Changes.......: ;==================================================================== Release 3.0 - 20120501 -Code re-write to improve performance and increase features -Added ID3v2.4 read/write compatability -Added removal of Unsynchronisation (Needed for ID3v2.4 tags from Foobar2000) -Added reading/writing of ID3v1.1+ Extended Tags (Not Tested Yet) (http://en.wikipedia.org/wiki/ID3) -Added reading and removing of APEv2 Tags at the end of the file -Improved time to read tags for all versions by about 80% -Added ability to read/write multiple frameIDs (ID3v2 allows multiple TXXX,WXXX frames and others) -Added functions to read ID3v2 Header information -Added function to return binary data -Removed _ID3GetFrameDescription() (not needed for reading/writing of tags) Release 3.1 - 20120519 -Fixed _h_ID3v2_GetFrameAPIC() to handle png file data properly -Added a check if ID3v2 exists and if not to add it in _ID3v2Frame_SetBinary() and _ID3v2Frame_SetFields() -Fixed removing ID3v1 Tag in _ID3v1Tag_RemoveTag(), changed first FileOpen to read mode instead of write mode -Fixed _APEv2Tag_ReadFromFile() when no ID3v1 tag exists -Added _h_ID3v2FrameHeader_GetFlags() functionality -Fixed _h_ID3v2_GetFrameAPIC() when reading an ID3v2.2 PIC frame Release 3.2 - 20120529 -Fixed _ID3v2Tag_WriteToFile() to write new tag data -Changed _ID3v2Tag_GetFrameIDs() to _h_ID3v2Tag_EnumerateFrameIDs() because it should only be used as an internal helper function -Changed _ID3v2Tag_RemoveUnsynchronisation() to _h_ID3v2Tag_RemoveUnsynchronisation() because it should only be used as an internal helper function -Changed _MPEG_IsValidHeader() to _h_MPEG_IsValidHeader() because it should only be used as an internal helper function -Added _APEv2 functions _APEv2Tag_GetTagSize() _APEv2Tag_GetVersion() and _APEv2Tag_GetItemCount() -Fixed _APEv2Tag_ReadFromFile() to read in tag plus header -Changed _APEv2_GetItemKeys() to _h_APEv2_EnumerateItemKeys() because it should only be used as an internal helper function -Changed _APEv2_GetItemKeys() function return structure to be a delimited list of the ItemKeys or an array -Added support in _APEv2_GetItemValueString for reading of multiple values in one APEv2 ItemKey seperated by 0x00 (Support for Mp3tag) -Added support in _APEv2_GetItemValueString for reading Album Art -Added support for iTunes APEv2 TagSize which includes header (Not to standard) -Added _MPEG_... functions -Tested Reading Tags from -Tag&Rename - ID3v1.1 & ID3v2.3 -mp3Tag Pro - ID3v1.1 & ID3v2.3 -Mp3tag - ID3v1.1, ID3v2.3 & APEv2 Release 3.3 - 20120608 -Fixed ID3v2 _ID3v2Frame_SetFields() and _ID3v2Tag_WriteToFile() if called when no tag exists -Added a check if tag exists in _ID3v1Field_SetString() and creates it if it does not exist -Changed _ID3v2Frame_SetFields() for TXXX so that the delimited string format is $sDescription:$sValue, where more then 1 field in string is passed in -Commented some message boxes used for debugging Release 3.4 - 20120610 -Added resetting of all global ID3 variables in _ID3ReadTag() to make sure they are set back to the default values and ready to read a new file -changed _ID3WriteTag() to determine what tags have been read and to only write back those version if $iTagVersion=-1, edited _ID3WriteTag() comments -added $iReturnTypeFlag to _ID3GetTagField() inputs for ID3v2 functions -fixed _ID3GetTagField() so that @extended is set to the Number of frames that exist with same $sFrameIDRequest -fixed _h_ID3v2_EncodeStringToBinary() variable typo ($FrameData should be $bFrameData), thanks BrewManNH! Release 3.5 TODO -TODO -MPEG - handle VBR MP3 properly (XING Header) and (VBRI Header) -http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#SideInfo -TODO -ID3v2.4 - work with COMM frame from Foobar2000 -TODO -ID3v1 - Test Read/write of TAG+ Extended Tags -TODO -ID3v2 - Add reading of extended header -TODO -ID3v2.4 add ability to remove unsychronization of each frame -TODO -add error flow up from _ID3v1 and _ID3v2 functions to _ID3 functions -TODO Clean up code comments #ce ;======================================================================================================== Dim $ID3Filenames = "" Dim $ID3v1_RawDataBinary = 0, $ID3v1Plus_RawDataBinary = 0, $ID3v2_RawDataBinary = 0, $APEv2_RawDataBinary = 0 Dim $ID3v2_TagFrameString = "",$APEv2_TagFrameString = "", $ID3v2_OriginalTagSize = 0 ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3ReadTag($Filename, $iVersion = 0) ; Description: Reads ID3v1, ID3v2, and APEv2 binary data and stores them into global variables ; Parameter(s): $Filename - Filename of mp3 file include full path ; $iTagVersion - ID3 Version to read (Default: 0 => ID3v1 & ID3v2) ; 0 => Read ID3v1 & ID3v2 & APEv2 ; 1 => Read ID3v1 only ; 2 => Read ID3v2 only ; 3 => Read ID3v1 & ID3v2 ; 4 => Read APEv2 only ; Requirement(s): This function must be used first in order to use other ID3.au3 functions. ; Return Value(s): On Success - Returns a string that denotes the tags and fields that exist in file. ; @extended = (Can be a combination of the following:) ; 0 -> No Tags Found ; 1 -> ID3v1 Found ; 2 -> ID3v2 Found ; 4 -> APEv2 Found ; - Example Returned String, ; "FilePath\Filename.mp3 @CRLF ; ID3v2.3.0|TIT2:1|TPE1:1|TALB:1|TRCK:1|TYER:1|COMM:2|APIC:1|... @CRLF ; ID3v1.1|Title|Artist|Album|Year|Comment|Track|Genre|... @CRLF ; APEv2|ARTIST|ALBUM|GENRE|..." ; Note: for ID3v2 the number that follows the colon indicates the number of ; Frames that were found with the same FrameIDs. ; On Failure - Returns 0 and @error = 1 for File not found ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ; ;========================================================================================== Func _ID3ReadTag($Filename, $iTagVersion = 0) ;Reset all global vairables $ID3Filenames = "" $ID3v1_RawDataBinary = 0 $ID3v1Plus_RawDataBinary = 0 $ID3v2_RawDataBinary = 0 $APEv2_RawDataBinary = 0 $ID3v2_TagFrameString = "" $APEv2_TagFrameString = "" $ID3v2_OriginalTagSize = 0 Local $sTAGINFO = $Filename & @CRLF, $iTagVersionsFound = 0 $ID3v2_TagFrameString = $Filename If Not(FileExists($Filename)) Then SetError(1) Return 0 EndIf ;Read ID3 Data from file and store in $ID3v2_RawDataBinary, $ID3v1_RawDataBinary, $APEv2_RawDataBinary ;**************************************************************************************** Switch $iTagVersion Case 0 ;All Tags $sTAGINFO &= _ID3v2Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended $sTAGINFO &= _h_ID3v2Tag_EnumerateFrameIDs() $sTAGINFO &= _ID3v1Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended $sTAGINFO &= _APEv2Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended Case 1 ;ID3v1 only $sTAGINFO &= _ID3v1Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended Case 2 ;ID3v2 only $sTAGINFO &= _ID3v2Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended $sTAGINFO &= _h_ID3v2Tag_EnumerateFrameIDs() Case 3 ;ID3v1 and ID3v2 only $sTAGINFO &= _ID3v2Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended $sTAGINFO &= _h_ID3v2Tag_EnumerateFrameIDs() $sTAGINFO &= _ID3v1Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended Case 4 ;APEv2 only $sTAGINFO &= _APEv2Tag_ReadFromFile($Filename) $iTagVersionsFound += @extended EndSwitch ;**************************************************************************************** SetExtended($iTagVersionsFound) Return $sTAGINFO EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3GetTagField($sFrameIDRequest, $iFrameID_Index = 1) ; Description: Returns the simple string from ID3 Tag Data. ; For ID3v2 complex FrameID where multple strings make up the Frame (ie. TXXX) use _ID3v2Frame_GetFields(). ; For ID3v2 FrameIDs that are not implimented within this UDF (ie. PRIV) use _ID3v2Frame_GetBinary() to get the raw frame data. ; For APEv2 tag Strings use _APEv2_GetItemValueString() ; Parameter(s): $sFrameIDRequest - ID3 FrameID String of the Field to return (ie. "TIT2" for ID3v2 Title or "Title" for ID3v1 Title) ; Valid ID3v1 FrameIDs to Request, ; Title | Artist | Album | Year | Comment | Track | Genre | Version1 | Speed | Start-Time | End-Time ; Valid ID3v2 FrameIDs to Request - see specification for four character FrameIDs ; $iFrameID_Index - Index number of ID3v2 FrameID, COMM, APIC and TXXX (and more) can exist multiple time in ID3v2 Tag ; Requirement(s): Must call _ID3ReadTag() first. ; Return Value(s): On Success - Returns Field String. ; @error = 0; @extended = Number of Frames that exist in the ID3v2 Tag with $sFieldIDRequest ; On Failure - Returns Empty String meaning $sFieldIDRequest did not match any IDs in the mp3 File ; @error = 1; @extended = 0 ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ;============================================================================================ Func _ID3GetTagField($sFrameIDRequest, $iFrameID_Index = 1,$iReturnTypeFlag = 0) Local $vFrameString, $iNumFrames = 0, $iError = 0 If StringInStr("|Title|Artist|Album|Year|Comment|Track|Genre|Version1|Speed|Start-Time|End-Time",$sFrameIDRequest,1) Then $vFrameString = _ID3v1Field_GetString($sFrameIDRequest) $iError = @error $iNumFrames = 1 Else $vFrameString = _ID3v2Frame_GetFields($sFrameIDRequest,$iFrameID_Index,$iReturnTypeFlag) $iNumFrames = @extended $iError = @error EndIf SetError($iError) SetExtended($iNumFrames) Return $vFrameString EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3SetTagField() ; Description: Sets a simple string to the requested Tag Frame/Field. Use _ID3WriteTag() to write updated tag to file. ; For ID3v2 complex FrameID where multple strings make up the Frame (ie. TXXX) use _ID3v2Frame_GetFields(). ; For ID3v2 FrameIDs that are not implimented within this UDF (ie. PRIV) use _ID3v2Frame_SetBinary() to get the raw frame data. ; APEv2 Tags not implimented yet. ; Parameter(s): $sFieldIDRequest - ID3 Field ID String of the Field to return (ie. "TIT2" for ID3v2 Title or "Title" for ID3v1 Title) ; $sFieldValue - Simple text string (when $sFieldValue is set to "" a blank string frame will be removed from the ID3v2 Tag) ; $iFrameID_Index - Index number of ID3v2 FrameID, COMM, APIC and TXXX (and more) can exist multiple time in ID3v2 Tag ; Requirement(s): None ; Return Value(s): On Success - Returns 0. ; @error = 0; @extended = 0 ; On Failure - ??? ; @error = 1; @extended = 0 ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ;============================================================================================ Func _ID3SetTagField($sFrameIDRequest, $sFieldValue, $iFrameID_Index = 1) If StringInStr("|Title|Artist|Album|Year|Comment|Track|Genre|Version1|Speed|Start-Time|End-Time",$sFrameIDRequest,1) Then _ID3v1Field_SetString($sFrameIDRequest,$sFieldValue) Return 0 EndIf _ID3v2Frame_SetFields($sFrameIDRequest,$sFieldValue,$iFrameID_Index) Return 0 EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3WriteTag() ; Description: Writes the ID3 Tag Data to the mp3 file ; Parameter(s): $sFilename - Filename of mp3 file include full path ; $iTagVersion - Tag version to write (Default = -1, will try to determine what tag data exists and to write it) ; Requirement(s): None ; Return Value(s): On Success - Returns the versions that got written to the file ($iTagVersion), value of 0 means both ID3v1 and ID3v2. ; @error = 0 ; On Failure - Returns $iTagVersion ; @error = 1 ; Author ........: joeyb1275 ; Modified.......: 20120609 by joeyb1275 ;============================================================================================ Func _ID3WriteTag($sFilename,$iTagVersion = -1) ;try to determine what tags have been read into memory If $iTagVersion == -1 Then Local $ID3v1_TagFound = False, $ID3v2_TagFound = False Local $ID3v1_ID = BinaryToString(BinaryMid($ID3v1_RawDataBinary,1,3)) If StringCompare($ID3v1_ID,"TAG") == 0 Then $ID3v1_TagFound = True $iTagVersion = 1 EndIf Local $sID3v2_TagID = BinaryToString(BinaryMid($ID3v2_RawDataBinary,1,3)) If StringCompare($sID3v2_TagID,"ID3") == 0 Then $ID3v2_TagFound = True $iTagVersion = 2 EndIf If $ID3v1_TagFound and $ID3v2_TagFound Then $iTagVersion = 0 EndIf EndIf Switch $iTagVersion Case -1 SetError(1) Case 0 ;Wite both ID3v1 and ID3v2 Tags _ID3v1Tag_WriteToFile($sFilename) _ID3v2Tag_WriteToFile($sFilename) SetError(0) Case 1 ;Wite ID3v1 _ID3v1Tag_WriteToFile($sFilename) SetError(0) Case 2 ;Wite ID3v2 _ID3v2Tag_WriteToFile($sFilename) SetError(0) EndSwitch Return $iTagVersion EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3RemoveTag() ; Description: Reads the ID3 Tag Data from an mp3 file and stores it in a Buffer and returns the Field Requested ; Parameter(s): $sFilename - Filename of mp3 file include full path ; $iTagVersion - ID3 Field ID String of the Field to return (ie. "TIT2" for ID3v2 Title or "Title" for ID3v1 Title) ; Requirement(s): None ; Return Value(s): On Success - Returns Field String. If multiple fields found string is delimited with @CRLF. ; @error = 0; @extended = Number of Frames that exist in the ID3v2 Tag with $sFieldIDRequest ; On Failure - Returns Empty String meaning $sFieldIDRequest did not match any IDs in the mp3 File ; @error = 1; @extended = 0 ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ;============================================================================================ Func _ID3RemoveTag($sFilename, $iTagVersion) Switch $iTagVersion Case 0 ;All Tags _ID3v1Tag_RemoveTag($sFilename) _ID3v2Tag_RemoveTag($sFilename) _APEv2_RemoveTag($sFilename) Case 1 ;ID3v1 only _ID3v1Tag_RemoveTag($sFilename) Case 2 ;ID3v2 only _ID3v2Tag_RemoveTag($sFilename) Case 3 ;ID3v1 and ID3v2 only _ID3v1Tag_RemoveTag($sFilename) _ID3v2Tag_RemoveTag($sFilename) Case 4 ;APEv2 only _APEv2_RemoveTag($sFilename) EndSwitch EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3CreateTag() ; Description: Creates a new empty Tag (ID3v1 or ID3v2). Use _ID3WriteTag() to write new tag to file. ; Parameter(s): $iTagVersion - Filename of mp3 file include full path ; Requirement(s): None ; Return Value(s): On Success - Returns Field String. If multiple fields found string is delimited with @CRLF. ; @error = 0; @extended = Number of Frames that exist in the ID3v2 Tag with $sFieldIDRequest ; On Failure - Returns Empty String meaning $sFieldIDRequest did not match any IDs in the mp3 File ; @error = 1; @extended = 0 ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ;============================================================================================ Func _ID3CreateTag($iTagVersion=0,$iTagSubVersion = -1) Switch $iTagVersion Case 0 ;All Tags _ID3v1Tag_CreateTag() _ID3v2Tag_CreateTag(4) ;TODO APEv2 Case 1 ;ID3v1 only _ID3v1Tag_CreateTag() Case 2 ;ID3v2 only If $iTagSubVersion ==-1 Then $iTagSubVersion = 4 _ID3v2Tag_CreateTag($iTagSubVersion) Case 3 ;ID3v1 and ID3v2 only _ID3v1Tag_CreateTag() _ID3v2Tag_CreateTag(4) Case 4 ;APEv2 only ;TODO EndSwitch EndFunc ; #FUNCTION# ;=============================================================================== ; Function Name: _ID3DeleteFiles() ; Description: Deletes any files created by ID3.au3 (ie. AlbumArt.jpeg and SongLyrics.txt) ; Parameter(s): None ; Requirement(s): None ; Return Value(s): On Success - Returns 1. ; On Failure - Returns 0 ; Author ........: joeyb1275 ; Modified.......: 20120501 by joeyb1275 ;============================================================================================ Func _ID3DeleteFiles() If $ID3Filenames == "" Then Return 1 $aID3File = StringSplit($ID3Filenames,"|") For $i = 1 To $aID3File[0] If FileExists($aID3File[$i]) Then $ret = FileDelete($aID3File[$i]) If $ret == 0 Then Return 0 EndIf Next $ID3Filenames = "" Return 1 EndFunc Func _ID3v1Tag_ReadFromFile($Filename) ;TODO - SetExtended and SetError ;Add reading of ID3v1 Extended Tag - http://en.wikipedia.org/wiki/ID3 Local $ID3v1_TAGINFO = "", $ID3v1_TagFound = False, $ID3v1Plus_TagFound = False Local $hfile = FileOpen($Filename,16) ;open in binary mode FileSetPos($hfile, -128, $FILE_END) $ID3v1_RawDataBinary = FileRead($hfile) ;ID3v1 Extended Tag Test Code FileSetPos($hfile, -(128+227), $FILE_END) ;include 227 bytes to check for Extended tag Header $ID3v1Plus_RawDataBinary = FileRead($hfile,227) FileClose($hfile) $ID3v1PlusID = BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,1,4)) If StringCompare($ID3v1PlusID,"TAG+") == 0 Then $ID3v1Plus_TagFound = True ;~ MsgBox(0,"$ID3v1PlusID",$ID3v1PlusID) Else $ID3v1Plus_RawDataBinary = Binary(0) EndIf Local $ID3v1_ID = BinaryToString(BinaryMid($ID3v1_RawDataBinary,1,3)) ;MsgBox(0,"ID3v1 Tag ID",$ID3v1ID) If StringCompare($ID3v1_ID,"TAG") == 0 Then $ID3v1_TagFound = True EndIf If $ID3v1_TagFound and Not $ID3v1Plus_TagFound Then $Version = _ID3v1Tag_GetVersion() $ID3v1_TAGINFO = "ID3v1." & $Version If $Version == "0" Then $ID3v1_TAGINFO &= "|Title|Artist|Album|Year|Comment|Genre|Version1" & @CRLF Else $ID3v1_TAGINFO &= "|Title|Artist|Album|Year|Comment|Track|Genre|Version1" & @CRLF EndIf ElseIf $ID3v1_TagFound And $ID3v1Plus_TagFound Then $Version = _ID3v1Tag_GetVersion() $ID3v1_TAGINFO = "ID3v1." & $Version & "+" If $Version == "0" Then $ID3v1_TAGINFO &= "|Title|Artist|Album|Year|Comment|Genre|Version1|Speed|Start-Time|End-Time" & @CRLF Else $ID3v1_TAGINFO &= "|Title|Artist|Album|Year|Comment|Track|Genre|Version1|Speed|Start-Time|End-Time" & @CRLF EndIf Else $ID3v1_RawDataBinary = Binary(0) EndIf If $ID3v1_TagFound Then SetExtended(1) EndIf Return $ID3v1_TAGINFO EndFunc Func _ID3v1Tag_GetVersion() Local $Track = Dec(StringTrimLeft(BinaryMid($ID3v1_RawDataBinary,126,2),2)) If $Track == 0 Then Return "0" Else Return "1" EndIf EndFunc Func _ID3v1Field_GetString($FieldName) ;~ ID3v1: 128 bytes ;~ Field Length Description ;~ header 3 "TAG" ;~ title 30 30 characters of the title ;~ artist 30 30 characters of the artist name ;~ album 30 30 characters of the album name ;~ year 4 A four-digit year ;~ comment 28 or 30 The comment. ;~ zero-byte 1 If a track number is stored, this byte contains a binary 0. ;~ track 1 The number of the track on the album, or 0. Invalid, if previous byte is not a binary 0. ;~ genre 1 Index in a list of genres, or 255 ;~ Extended tag (placed before the ID3v1 tag): 227 bytes ;~ Field Length Description ;~ header 4 "TAG+" ;~ title 60 Next 60 characters of the title (90 characters total) ;~ artist 60 Next 60 characters of the artist name ;~ album 60 Next 60 characters of the album name ;~ speed 1 0=unset, 1=slow, 2= medium, 3=fast, 4=hardcore ;~ genre 30 A free-text field for the genre ;~ start-time 6 the start of the music as mmm:ss ;~ end-time 6 the end of the music as mmm:ss Local $FieldString = "", $ID3v1Plus_TagFound = False If BinaryLen($ID3v1_RawDataBinary) <= 4 Then ;Tag does not exist Return $FieldString EndIf If BinaryLen($ID3v1Plus_RawDataBinary) > 4 Then $ID3v1Plus_TagFound = True EndIf Switch $FieldName Case "Title" $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,4,30)) If $ID3v1Plus_TagFound Then $FieldString &= BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,4,60)) EndIf Case "Artist" $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,34,30)) If $ID3v1Plus_TagFound Then $FieldString &= BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,64,60)) EndIf Case "Album" $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,64,30)) If $ID3v1Plus_TagFound Then $FieldString &= BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,124,60)) EndIf Case "Year" $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,94,4)) Case "Comment" Local $Track = Dec(Hex(BinaryMid($ID3v1_RawDataBinary,126,2))) If $Track < 1000 And $Track > 0 Then $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,98,28)) Else $FieldString = BinaryToString(BinaryMid($ID3v1_RawDataBinary,98,30)) EndIf Case "Track" Local $Track = Dec(Hex(BinaryMid($ID3v1_RawDataBinary,126,2))) If $Track < 1000 And $Track > 0 Then $FieldString = $Track Else $FieldString = "" EndIf Case "Genre" Local $GenreID = Dec(Hex(BinaryMid($ID3v1_RawDataBinary,128,1))) $FieldString = _h_ID3v1_GetGenreFromID($GenreID) If $ID3v1Plus_TagFound Then $FieldString = BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,185,30)) EndIf Case "Version1" Local $Track = Dec(Hex(BinaryMid($ID3v1_RawDataBinary,126,2))) If $Track == 0 Then $FieldString = "ID3v1.0" Else $FieldString = "ID3v1.1" EndIf Case "Speed" ;TAG+ ;0=unset, 1=slow, 2= medium, 3=fast, 4=hardcore If $ID3v1Plus_TagFound Then Switch BinaryMid($ID3v1Plus_RawDataBinary,184,1) Case 0 $FieldString = "unset" Case 1 $FieldString = "slow" Case 2 $FieldString = "medium" Case 3 $FieldString = "fast" Case 4 $FieldString = "hardcore" Case Else $FieldString = BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,184,1)) EndSwitch EndIf Case "Start-Time" ;TAG+ If $ID3v1Plus_TagFound Then $FieldString = BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,215,6)) EndIf Case "End-Time" ;TAG+ If $ID3v1Plus_TagFound Then $FieldString = BinaryToString(BinaryMid($ID3v1Plus_RawDataBinary,221,6)) EndIf EndSwitch Return $FieldString EndFunc Func _ID3v1Field_SetString($sFieldName,$sFieldString,$iPaddingType = -1) ;TODO Add Comments ;$iPaddingType = -1 -> uses existing padding type ;$iPaddingType = 0 -> uses 0x00 to pad ;$iPaddingType = 1 -> uses spaces to pad 0x20 Local $bID3v1_RawDataBinary_Temp, $bPad ;Create tag if it does not exist Local $ID3v1_ID = BinaryToString(BinaryMid($ID3v1_RawDataBinary,1,3)) If StringCompare($ID3v1_ID,"TAG") <> 0 Then _ID3v1Tag_CreateTag() EndIf Switch $iPaddingType Case 0 $bPad = Binary("0x00") $sFieldString = StringStripWS($sFieldString,2) Case 1 $bPad = Binary("0x20") Case Else ;includes -1 If StringCompare(StringRight($sFieldString,1)," ") Then $bPad = Binary("0x20") Else $bPad = Binary("0x00") EndIf EndSwitch Switch $sFieldName Case "Title" $sFieldString = StringLeft($sFieldString,30) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,3) & Binary($sFieldString) For $iPAD = 1 to (30 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,34) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Artist" $sFieldString = StringLeft($sFieldString,30) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,33) & Binary($sFieldString) For $iPAD = 1 to (30 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,64) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Album" $sFieldString = StringLeft($sFieldString,30) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,63) & Binary($sFieldString) For $iPAD = 1 to (30 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,94) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Year" $sFieldString = StringLeft($sFieldString,4) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,93) & Binary($sFieldString) For $iPAD = 1 to (4 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,98) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Comment" $sFieldString = StringLeft($sFieldString,28) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,97) & Binary($sFieldString) For $iPAD = 1 to (28 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,126) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Track" $sFieldString = Binary("0x" & Hex(Number($sFieldString),4)) $bID3v1_RawDataBinary_Temp = BinaryMid($ID3v1_RawDataBinary,1,125) & $sFieldString For $iPAD = 1 to (2 - BinaryLen($sFieldString)) $bID3v1_RawDataBinary_Temp &= $bPad Next $bID3v1_RawDataBinary_Temp &= BinaryMid($ID3v1_RawDataBinary,128) $ID3v1_RawDataBinary = $bID3v1_RawDataBinary_Temp Case "Genre" Local $GenreID = _h_ID3v1_GetGenreID($sFieldString) $GenreID = Binary("0x" & Hex($GenreID,2)) $ID3v1_RawDataBinary = BinaryMid($ID3v1_RawDataBinary,1,127) & $GenreID EndSwitch EndFunc Func _ID3v1Tag_WriteToFile($sFilename) Local $bID3v1TagToWrite = $ID3v1_RawDataBinary, $iFileSetPos = 0 ;Check if tag exists If _ID3v1Tag_ReadFromFile($sFilename) <> "" Then $iFileSetPos = 128 EndIf ;Write MP3 Data $hFile = FileOpen($sFilename,16+1) ;Open for write force binary $Test = FileSetPos($hFile, $iFileSetPos, $FILE_END) ;Go to End of File (-128 if tag exists) FileWrite($hFile,$bID3v1TagToWrite) ;Write new tag FileClose($hFile) $ID3v1_RawDataBinary = $bID3v1TagToWrite EndFunc Func _ID3v1Tag_RemoveTag($sFilename) Local $bFileData, $hOldFile, $hNewFile If _ID3v1Tag_ReadFromFile($sFilename) <> "" Then $ID3v1_RawDataBinary = Binary("0x00") $hOldFile = FileOpen($sFilename,16) ;Open for read force binary $bFileData = FileRead($hOldFile) ;Read all file data FileClose($hOldFile) $bFileData = BinaryMid($bFileData,1,BinaryLen($bFileData) - 128) ;Remove ID3v1 Tag $hNewFile = Fileopen($sFilename,2+8+16) ;Open File and Erase all FileWrite($hNewFile,$bFileData) ;Write all Data less ID3v1 Tag FileClose($hNewFile) EndIf EndFunc Func _ID3v1Tag_CreateTag() ;Creates an empty ID3v1 Tag $ID3v1_RawDataBinary = Binary("TAG") For $i=1 To 125 $ID3v1_RawDataBinary &= Binary("0x00") Next EndFunc Func _h_ID3v1_GetGenreFromID($iID) ;Author: YDY (Lazycat) Thank you for writing all this out 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 Func _h_ID3v1_GetGenreID($sGrenre) 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", ",") For $i = 1 to $asGenre[0] If $sGrenre == $asGenre[$i] Then Return $i - 1 EndIf Next Return 12 ;Other EndFunc Func _ID3v2Tag_ReadFromFile($sFilename) Local $bID3v2_TagData, $sTAGINFO = "" Local $hID3v2_File = FileOpen($sFilename,16) ;mode = Force binary $ID3v2_TagFrameString = "" If Not(FileExists($sFilename)) Then SetError(1) Return $sTAGINFO EndIf ;Local $bID3v2_TagHeader = FileRead($hID3v2_File, 10) ;not true for ID3v2.2 $ID3v2_RawDataBinary = FileRead($hID3v2_File, 10) ;not true for ID3v2.2 Local $sID3v2_TagID = BinaryToString(BinaryMid($ID3v2_RawDataBinary,1,3)) If (StringCompare($sID3v2_TagID,"ID3") <> 0) Then FileClose($hID3v2_File) SetError(1) Return $sTAGINFO EndIf If $ID3v2_RawDataBinary == -1 Then ;ID3v2 Tag was not found FileClose($hID3v2_File) SetError(1) Return $sTAGINFO EndIf Local $iID3v2_TagSize = _ID3v2Tag_GetTagSize() ;~ MsgBox(0,"$iID3v2_TagSize",$iID3v2_TagSize) $ID3v2_OriginalTagSize = $iID3v2_TagSize ;Read in Rest of ID3v2 Tag Data $ID3v2_RawDataBinary &= Binary(FileRead($hID3v2_File,$iID3v2_TagSize)) ;~ $ID3v2_RawDataBinary &= Binary(FileRead($hID3v2_File,50)) ;add 50 to get MPEG Header FileClose($hID3v2_File) $sTAGINFO = "ID3v2." & _ID3v2Tag_GetVersion() If _ID3v2Tag_GetHeaderFlags("Unsynchronisation") == 1 Then _h_ID3v2Tag_RemoveUnsynchronisation() EndIf SetError(0) SetExtended(2) Return $sTAGINFO EndFunc Func _h_ID3v2Tag_EnumerateFrameIDs() Local $bFrameHeader, $iTagVersion = 3, $iFrameSize, $sFrameID = "", $iZPAD = 0, $sID3v2_TAGINFO = "" Local $iReadBytesOffset = 10, $iFrameHeaderLen Local $iTagSize = _ID3v2Tag_GetTagSize() $ID3v2_TagFrameString = "" If _ID3v2Tag_GetHeaderFlags("ExtendedHeader") <> 0 Then ;ID3v2_TagSize include this ExtendedHeader ;~ MsgBox(0,"_GetID3v2_TagFlags",_ID3v2Tag_GetHeaderFlags("ExtendedHeader")) $iReadBytesOffset += 10 EndIf Local $iBytesRead = $iReadBytesOffset ;MsgBox(0,"Version",_ID3v2Tag_GetVersion()) Local $iTagVersion = Number(StringLeft(_ID3v2Tag_GetVersion(),1)) If $iTagVersion == 2 Then $iFrameHeaderLen = 6 Else $iFrameHeaderLen = 10 EndIf ;Scan Tag for all Fields While $iBytesRead < $iTagSize $bFrameHeader = BinaryMid($ID3v2_RawDataBinary,$iBytesRead + 1,$iFrameHeaderLen) $iBytesRead += $iFrameHeaderLen $sFrameID = _h_ID3v2FrameHeader_GetFrameID($bFrameHeader) ;~ MsgBox(0,$sFrameID,$bFrameHeader) ;Check for a valid frameID string ;**************************************************************************************** If $sFrameID <> -1 Then ;check flags $iFrameSize = _h_ID3v2FrameHeader_GetFrameSize($bFrameHeader) ;Test Code for Foobar2000 ID3v2.4 COMM tag is not conforming to Spec ;~ If _h_ID3v2FrameHeader_GetFlags($bFrameHeader, "Unsynchronisation") == 1 Then ;~ If _h_ID3v2FrameHeader_GetFlags($bFrameHeader, "DataLengthIndicator") == 1 Then ;~ MsgBox(0,"$iFrameSize",$iFrameSize) ;~ ;FrameSize is the number of bytes NOT including Unsynchronisation ;~ Local $iOriginalFrameSize = $iFrameSize ;~ For $ibytestep=0 To $iFrameSize ;~ MsgBox(0,"Binary",BinaryMid($ID3v2_RawDataBinary,$iBytesRead + 1 + $ibytestep,3)) ;~ If BinaryMid($ID3v2_RawDataBinary,$iBytesRead + 1 + $ibytestep,1) == Binary("0xFF") Then ;~ If BinaryMid($ID3v2_RawDataBinary,$iBytesRead + 1 + $ibytestep + 1,1) == Binary("0x00") Then ;~ $iFrameSize += 1 ;~ $iBytesRead += 1 ;~ MsgBox(0,"$iFrameSize",$iFrameSize) ;~ EndIf ;~ EndIf ;~ Next ;~ Else ;~ ;FrameSize is the number of bytes including Unsynchronisation ;~ EndIf ;~ EndIf Else If Dec(Hex(BinaryMid($bFrameHeader,1,2),4)) == 0 Then ;~ MsgBox(0,"$bFrameHeader",$bFrameHeader) $iZPAD = ($iTagSize + 10) - ($iBytesRead - $iFrameHeaderLen) ;~ MsgBox(0,"ZPAD Start",BinaryToString(BinaryMid($ID3v2_RawDataBinary,$iBytesRead - $iFrameHeaderLen))) $ID3v2_TagFrameString &= "ZPAD" & "|" & String($iBytesRead - $iFrameHeaderLen + 1) & "|" & $iZPAD & @CRLF ExitLoop Else ;~ MsgBox(0,"Error Check Scan Frame 1",$sFrameID) ;Need to error check scan ;~ MsgBox(0,"$bFrameHeader",$bFrameHeader) ;This could be ZPAD with first byte wrong ;~ MsgBox(0,"$iBytesRead",$iBytesRead & " of " & $iTagSize) ;~ MsgBox(0,"20 bytes",BinaryMid($ID3v2_RawDataBinary,$iBytesRead - 6,20)) $iBytesRead -= Round(($iFrameSize/8 + 2)) ;~ MsgBox(0,"$iBytesRead",$iBytesRead) For $itest = 1 To ($iBytesRead + $iFrameSize + 20) $bFrameHeader = BinaryMid($ID3v2_RawDataBinary,$iBytesRead + 1 + $itest,$iFrameHeaderLen) $sFrameID = _h_ID3v2FrameHeader_GetFrameID($bFrameHeader) If $sFrameID <> -1 Then $iBytesRead += $itest ;~ MsgBox(0,"$iBytesRead Really in Frame",$itest) $iFrameSize = _h_ID3v2FrameHeader_GetFrameSize($bFrameHeader) ;~ MsgBox(0,"$FrameSize",$iFrameSize) ExitLoop 1 Else ;~ MsgBox(0,"Error Check Scan Frame",String($iBytesRead + 1 + $itest) & " => " & $FrameID) EndIf Next EndIf EndIf ;**************************************************************************************** $ID3v2_TagFrameString &= $sFrameID & "|" & String($iBytesRead + 1) & "|" & String($iFrameSize) & @CRLF ;~ MsgBox(0,"$ID3v2_TagFrameString",$ID3v2_TagFrameString) $iBytesRead += $iFrameSize ;~ MsgBox(0,"$iBytesRead",$iBytesRead & " of " & $iTagSize) $sFrameIndex = StringInStr($sID3v2_TAGINFO,$sFrameID) If $sFrameIndex == 0 Then $sID3v2_TAGINFO &= "|" & $sFrameID & ":" & "1" Else $sFrameIndexEnd = StringInStr(StringMid($sID3v2_TAGINFO,$sFrameIndex),":") $iNumFrameID = 1 + Number(StringMid($sID3v2_TAGINFO,$sFrameIndex + $sFrameIndexEnd,1)) $sNewFrameIDString = $sFrameID & ":" & $iNumFrameID $sID3v2_TAGINFO = StringReplace($sID3v2_TAGINFO,$sFrameID & ":" & $iNumFrameID-1,$sNewFrameIDString) EndIf WEnd ;TODO Check MPEG Header ;~ Local $sMPEG_TagHeader = _MPEG_GetTagHeader() ;~ If $sMPEG_TagHeader <> -1 Then ;~ $ID3v2_TagFrameString &= "MPEG" & "|" & $sMPEG_TagHeader & @CRLF ;~ EndIf Return $sID3v2_TAGINFO & @CRLF EndFunc Func _h_ID3v2Tag_RemoveUnsynchronisation() ;The only purpose of the 'unsynchronisation scheme' is to make the ID3v2 tag as compatible ;as possible with existing software. There is no use in 'unsynchronising' tags if the file ;is only to be processed by new software. Unsynchronisation may only be made with MPEG 2 ;layer I, II and III and MPEG 2.5 files. Whenever a false synchronisation is found within ;the tag, one zeroed byte is inserted after the first false synchronisation byte. The format ;of a correct sync that should be altered by ID3 encoders is as follows: ; %11111111 111xxxxx ;And should be replaced with: ; %11111111 00000000 111xxxxx ;This has the side effect that all $FF 00 combinations have to be altered, so they won't be ;affected by the decoding process. Therefore all the $FF 00 combinations have to be replaced ;with the $FF 00 00 combination during the unsynchronisation. To indicate usage of the ;unsynchronisation, the first bit in 'ID3 flags' should be set. This bit should only be set if ;the tag contains a, now corrected, false synchronisation. The bit should only be clear if the ;tag does not contain any false synchronisations. ;Find all %11111111 00000000 111xxxxx = 0xFF 0x00 0xE0 and 0xFF 0x00 0x00 Local $aIndex[1] $aIndex[0] = 0 For $ibyte=1 To BinaryLen($ID3v2_RawDataBinary)-1 If BinaryMid($ID3v2_RawDataBinary,$ibyte,1) == Binary("0xFF") Then If BinaryMid($ID3v2_RawDataBinary,$ibyte+1,1) == Binary("0x00") Then _ArrayAdd($aIndex,$ibyte) $aIndex[0] += 1 EndIf EndIf Next ;~ _ArrayDisplay($aIndex) Local $ID3v2_RawDataBinary_Temp = BinaryMid($ID3v2_RawDataBinary,1,5) Local $bCurrentTagFlags = BinaryMid($ID3v2_RawDataBinary,6,1) $ID3v2_RawDataBinary_Temp &= Binary("0x" & Hex(BitAND(127, Dec(Hex($bCurrentTagFlags,2))),2)) ;Clear unsynchronisation flag in Tag header $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,7,$aIndex[1]) Local $Start, $Length = $aIndex[1] For $ibyte = 1 To $aIndex[0]-1 $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,$aIndex[$ibyte] + 2,$aIndex[$ibyte+1]-$aIndex[$ibyte]-1) Next $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,$aIndex[$aIndex[0]]+2) $ID3v2_RawDataBinary = $ID3v2_RawDataBinary_Temp EndFunc Func _ID3v2Tag_WriteToFile($sFilename) Local $hNewFile, $hFile Local $bNewFileData = $ID3v2_RawDataBinary Local $bNewID3v2_RawDataBinary = $ID3v2_RawDataBinary ;Check if tag does not exist Local $sID3v2_TagID = BinaryToString(BinaryMid($ID3v2_RawDataBinary,1,3)) If (StringCompare($sID3v2_TagID,"ID3") <> 0) Then _ID3v2Tag_CreateTag(4) EndIf ;Write MP3 Data ;**************************************************************************************** $hFile = FileOpen($sFilename,16) ;Read mode force binary FileSetPos($hFile,$ID3v2_OriginalTagSize,0) ;Skip old tag data $bNewFileData &= FileRead($hFile) ;Read all file data after old tag FileClose($hFile) $hNewFile = Fileopen($sFilename,2+8+16) ;Open File and Erase all FileWrite($hNewFile,$bNewFileData) ;Write New Tag Data and rest of old file data FileClose($hNewFile) ;**************************************************************************************** $ID3v2_RawDataBinary = $bNewID3v2_RawDataBinary EndFunc Func _ID3v2Tag_RemoveTag($sFilename) Local $bFileData, $hFile, $hNewFile If _ID3v2Tag_ReadFromFile($sFilename) <> -1 Then $hFile = FileOpen($sFilename,16) ;Read mode force binary FileSetPos($hFile,_ID3v2Tag_GetTagSize(),0) ;Skip old tag data $bFileData = FileRead($hFile) ;Read all file data after tag FileClose($hFile) $hNewFile = Fileopen($sFilename,2+8+16) ;Open File and Erase all FileWrite($hNewFile,$bFileData) ;Write rest of file FileClose($hNewFile) EndIf EndFunc Func _ID3v2Tag_CreateTag($iVersion) ;Creates an empty ID3v2 Tag ;ID3v2/file identifier "ID3" ;ID3v2 version $03 00 ;ID3v2 flags %abc00000 ;ID3v2 size 4 * %0xxxxxxx ;ID3v2/file identifier $ID3v2_RawDataBinary = Binary("ID3") ;ID3v2 version Switch $iVersion Case 2 $ID3v2_RawDataBinary &= Binary("0x02") Case 3 $ID3v2_RawDataBinary &= Binary("0x03") Case 4 $ID3v2_RawDataBinary &= Binary("0x04") EndSwitch $ID3v2_RawDataBinary &= Binary("0x00") ;ID3v2 Flags set to zero $ID3v2_RawDataBinary &= Binary("0x00") ;ID3v2 Size (Tag Bytes - 10) not including tag header - SyncSafe integer $ID3v2_RawDataBinary &= Binary("0x00") & Binary("0x00") & Binary("0x00") & Binary("0x00") ;Could add ZPAD ;Must have at least one frame _ID3v2Frame_SetFields("TIT2"," ") EndFunc Func _ID3v2Tag_RemoveFrame($sFrameID, $iFrameID_Index = 1) _ID3v2Frame_SetBinary($sFrameID,-1,$iFrameID_Index) EndFunc Func _ID3v2Tag_GetVersion() ;Get Tag Version Local $sVersion = String(Number(BinaryMid($ID3v2_RawDataBinary,4,1))) & "." & String(Number(BinaryMid($ID3v2_RawDataBinary,5,1))) ;~ MsgBox(0,"$sVersion",$sVersion) Return $sVersion EndFunc Func _ID3v2Tag_GetHeaderFlags($sFlagReturnType = -1) ;ID3v2/file identifier "ID3" ;ID3v2 version $04 00 ;ID3v2 flags %abcd0000 ;ID3v2 size 4 * %0xxxxxxx ;ID3v2_Flags = %abc00000 ;a - Unsynchronisation ;b - Extended header ;c - Experimental indicator ;d - Footer present (ID3v2.4) Local $bTagFlags = BinaryMid($ID3v2_RawDataBinary,6,1) If $sFlagReturnType == "RawBinary" Then Return $bTagFlags EndIf Local $Unsynchronisation = BitShift(BitAND($bTagFlags,128),7) Local $ExtendedHeader = BitShift(BitAND($bTagFlags,64),6) Local $ExperimentalIndicator = BitShift(BitAND($bTagFlags,32),5) Local $Footer = BitShift(BitAND($bTagFlags,16),4) If Not Dec(Hex($bTagFlags)) == 0 Then ;~ MsgBox(0,"$ID3TagFlags", $bTagFlags) ;Test Code EndIf If $sFlagReturnType == -1 Then Return "Unsynchronisation" & "|" & $Unsynchronisation & @CRLF & _ "ExtendedHeader" & "|" & $ExtendedHeader & @CRLF & _ "ExperimentalIndicator" & "|" & $ExperimentalIndicator & @CRLF & _ "Footer" & "|" & $Footer EndIf Switch $sFlagReturnType Case "Unsynchronisation" Return $Unsynchronisation Case "ExtendedHeader" Return $ExtendedHeader Case "ExperimentalIndicator" Return $ExperimentalIndicator Case "Footer" Return $Footer Case Else SetError(1) Return $bTagFlags EndSwitch EndFunc Func _ID3v2Tag_GetTagSize($bTagHeaderData = -1) ;(ID3v2.3) The ID3v2 tag size is encoded with four bytes where the most ;significant bit (bit 7) is set to zero in every byte, making a total ;of 28 bits. The zeroed bits are ignored, so a 257 bytes long tag is ;represented as $00 00 02 01. ;(ID3v2.4) The ID3v2 tag size is stored as a 32 bit synchsafe integer (section ;6.2), making a total of 28 effective bits (representing up to 256MB). ;The ID3v2 tag size is the sum of the byte length of the extended ;header, the padding and the frames after unsynchronisation. If a ;footer is present this equals to ('total size' - 20) bytes, otherwise ;('total size' - 10) bytes. ;SyncSafe Integer ;0444 4333 0333 3322 0222 2221 0111 1111 ;0000 4444 3333 3333 2222 2222 1111 1111 (28 bits) Local $byte1, $byte2, $byte3, $byte4 If $bTagHeaderData == -1 Then If BinaryLen($ID3v2_RawDataBinary) < 10 Then Return 0 EndIf $byte1 = BitAND(BinaryMid($ID3v2_RawDataBinary,7,1),127) $byte2 = BitAND(BinaryMid($ID3v2_RawDataBinary,8,1),127) $byte3 = BitAND(BinaryMid($ID3v2_RawDataBinary,9,1),127) $byte4 = BitAND(BinaryMid($ID3v2_RawDataBinary,10,1),127) Else $byte1 = BitAND(BinaryMid($bTagHeaderData,7,1),127) $byte2 = BitAND(BinaryMid($bTagHeaderData,8,1),127) $byte3 = BitAND(BinaryMid($bTagHeaderData,9,1),127) $byte4 = BitAND(BinaryMid($bTagHeaderData,10,1),127) EndIf Local $bTagSize = BitShift($byte1,-21) + BitShift($byte2,-14) + BitShift($byte3,-7) + $byte4 Return Dec(Hex($bTagSize),2) EndFunc Func _ID3v2Tag_GetExtendedHeader($sReturnType = -1) ;From ID3v2.3 TagSpec ;Extended header size $xx xx xx xx ;Extended Flags $xx xx ;Size of padding $xx xx xx xx ;From ID32.4 TagSpec ;Extended header size 4 * %0xxxxxxx ;Number of flag bytes $01 ;Extended Flags $xx ;Where the 'Extended header size' is the size of the whole extended ;header, stored as a 32 bit synchsafe integer. An extended header can ;thus never have a size of fewer than six bytes. ;Need To Test ;check if extended header is valid in flag bits Local $bExtendedHeader, $sExtendedHeader = "" Local $iExtHeaderSize, $sExtFlagsbBin, $iSizeOfPadding, $iNumFlagBytes Switch _ID3v2Tag_GetVersion() Case "2.0" Case "3.0" ;~ $bExtendedHeader = BinaryMid($ID3v2_RawDataBinary,11,10) ;~ $iExtHeaderSize = Number(BinaryMid($ID3v2_RawDataBinary,11,4)) ;~ $sExtFlagsBin = _HexToBin_ID3(StringTrimLeft(BinaryMid($ID3v2_RawDataBinary,15,2),2)) ;~ $iSizeOfPadding = Number(BinaryMid($ID3v2_RawDataBinary,17,4)) ;~ $sExtendedHeader = "ExtendedHeaderSize" & "|" & $iExtHeaderSize & @CRLF & _ ;~ "ExtendedFlags" & "|" & $sExtFlagsBin & @CRLF & _ ;~ "SizeOfPadding" & "|" & $iSizeOfPadding Case "4.0" $bExtendedHeader = BinaryMid($ID3v2_RawDataBinary,11,10) ;MsgBox(0,"$bExtendedHeader",$bExtendedHeader) Case Else EndSwitch If $sReturnType == "Binary" Then Return BinaryMid($ID3v2_RawDataBinary,11,10) EndIf If $sReturnType == -1 Then Return $sExtendedHeader EndIf EndFunc Func _ID3v2Tag_GetFooter() ;Need to Test EndFunc Func _ID3v2Tag_GetZPAD() Local $iFrameInfo = StringInStr($ID3v2_TagFrameString,"ZPAD") Local $aFrameIDSplit = StringSplit($ID3v2_TagFrameString, "ZPAD", 1) Local $iNumFrameIDs = $aFrameIDSplit[0] - 1 ;~ _ArrayDisplay($aFrameIDSplit) Local $FrameInfoCut = StringMid($ID3v2_TagFrameString,$iFrameInfo) Local $iFrameInfoEnd = StringInStr($FrameInfoCut,@CRLF) $FrameInfoCut = StringMid($FrameInfoCut,1,$iFrameInfoEnd-1) $Firstpipe = StringInStr($FrameInfoCut,'|') $Lastpipe = StringInStr($FrameInfoCut,'|',-1,-1) Local $FrameStart = Number(StringMid($FrameInfoCut,$Firstpipe+1,($Lastpipe)-($Firstpipe+1))) Local $FrameSize = Number(StringMid($FrameInfoCut,$Lastpipe+1)) Return $FrameSize EndFunc Func _ID3v2Frame_GetBinary($sFrameID,$iFrameID_Index = 1, $fIncludeHeader = False) ;Return just the binary data of the Frame ;TODO include FRAMEID and Frame Header ;First check to make sure the tag was scanned for FrameIDs Local $aFrameSplit = StringSplit($ID3v2_TagFrameString, @CRLF, 1) Local $iNumFrames = $aFrameSplit[0] If $iNumFrames <= 1 Then _h_ID3v2Tag_EnumerateFrameIDs() EndIf ;First Get Start Byte and Frame Length from $ID3v2_TagFrameString Local $iFrameInfo = StringInStr($ID3v2_TagFrameString,$sFrameID,-1,$iFrameID_Index) Local $aFrameIDSplit = StringSplit($ID3v2_TagFrameString, $sFrameID, 1) Local $iNumFrameIDs = $aFrameIDSplit[0] - 1 If $iFrameInfo == 0 Then ;$iFrameID_Index was higher then Number of Frames that Exist SetExtended($iNumFrameIDs) Return -1 EndIf Local $FrameInfoCut = StringMid($ID3v2_TagFrameString,$iFrameInfo) Local $iFrameInfoEnd = StringInStr($FrameInfoCut,@CRLF) $FrameInfoCut = StringMid($FrameInfoCut,1,$iFrameInfoEnd-1) $Firstpipe = StringInStr($FrameInfoCut,'|') $Lastpipe = StringInStr($FrameInfoCut,'|',-1,-1) Local $FrameStart = Number(StringMid($FrameInfoCut,$Firstpipe+1,($Lastpipe)-($Firstpipe+1))) Local $FrameSize = Number(StringMid($FrameInfoCut,$Lastpipe+1)) ;~ MsgBox(0,$sFrameID,"FrameStart = " & $FrameStart) ;~ MsgBox(0,"$FrameSize",$FrameSize) If $fIncludeHeader Then $FrameStart = $FrameStart - 10 $FrameSize = $FrameSize + 10 EndIf Local $bFrameData = BinaryMid($ID3v2_RawDataBinary,$FrameStart,$FrameSize) ;~ MsgBox(0,"bFrameData",$bFrameData) If BinaryLen($bFrameData) <> 0 Then SetExtended($iNumFrameIDs) Return $bFrameData Else Return -1 EndIf EndFunc Func _ID3v2Frame_SetBinary($sFrameID,$bNewFrameData,$iFrameID_Index = 1) ;~ If $bNewFrameData == -1 then tag is to be removed ;$bNewFrameData must contain the header ;First see if this is a frame update or a new frame to be added Local $bFrameData = _ID3v2Frame_GetBinary($sFrameID,$iFrameID_Index,True) Local $fAddNewFrame = False If $bFrameData == -1 Then $fAddNewFrame = True EndIf If ($bNewFrameData == -1) and $fAddNewFrame Then ;Frame does not exist and user is trying to remove it Return 0 ;do nothing EndIf ;If ID3v2 does not exists then add new ID3v2.4 Tag If BinaryLen($ID3v2_RawDataBinary) < 10 Then _ID3v2Tag_CreateTag(4) EndIf ;Find differance in Frame Size to add or subtract from TagSize Local $iOldFrameSize = 0 Local $iNewFrameSize = 0 If $bNewFrameData <> -1 Then $iNewFrameSize = _h_ID3v2FrameHeader_GetFrameSize($bNewFrameData) + 10 EndIf If Not $fAddNewFrame Then $iOldFrameSize = _h_ID3v2FrameHeader_GetFrameSize(BinaryMid($bFrameData,1,10)) + 10 EndIf ;Set New TagSize Local $iOldTagSize = _ID3v2Tag_GetTagSize() Local $iNewTagSize = $iOldTagSize - $iOldFrameSize + $iNewFrameSize ;~ MsgBox(0,"TagSize","$iOldTagSize = " & $iOldTagSize & @CRLF & "$iNewTagSize = " & $iNewTagSize) ;Write TagSize (4 byte number) ;**************************************************************************************** ;SyncSafe Integer ;4444 4444 3333 3333 2222 2222 1111 1111 ;0444 4333 0333 3322 0222 2221 0111 1111 Local $bTagSize = Binary("0x" & Hex($iNewTagSize,8)) Local $byte1 = BitAND(BinaryMid($bTagSize,4,1),127) Local $byte2 = BitAND(BitShift(BinaryMid($bTagSize,3,1),-1) + BitShift(BinaryMid($bTagSize,4,1),7),127) Local $byte3 = BitAND(BitShift(BinaryMid($bTagSize,2,1),-2) + BitShift(BinaryMid($bTagSize,3,1),6),127) Local $byte4 = BitAND(BitShift(BinaryMid($bTagSize,1,1),-3) + BitShift(BinaryMid($bTagSize,2,1),5),127) Local $iTagSizeSyncSafe = BitShift($byte4,-24) + BitShift($byte3,-16) + BitShift($byte2,-8) + $byte1 Local $ID3v2_RawDataBinary_Temp = BinaryMid($ID3v2_RawDataBinary,1,6) $ID3v2_RawDataBinary_Temp &= Binary("0x" & Hex($iTagSizeSyncSafe,8)) If Not $fAddNewFrame Then ;Find Old Frame and Replace Local $iFrameInfo = StringInStr($ID3v2_TagFrameString,$sFrameID,-1,$iFrameID_Index) Local $aFrameIDSplit = StringSplit($ID3v2_TagFrameString, $sFrameID, 1) Local $iNumFrameIDs = $aFrameIDSplit[0] - 1 Local $FrameInfoCut = StringMid($ID3v2_TagFrameString,$iFrameInfo) Local $iFrameInfoEnd = StringInStr($FrameInfoCut,@CRLF) $FrameInfoCut = StringMid($FrameInfoCut,1,$iFrameInfoEnd-1) $Firstpipe = StringInStr($FrameInfoCut,'|') $Lastpipe = StringInStr($FrameInfoCut,'|',-1,-1) Local $FrameStart = Number(StringMid($FrameInfoCut,$Firstpipe+1,($Lastpipe)-($Firstpipe+1))) Local $FrameSize = Number(StringMid($FrameInfoCut,$Lastpipe+1)) Local $bOldFrameData = BinaryMid($ID3v2_RawDataBinary,$FrameStart,$FrameSize) If ($FrameStart - 20) > 1 Then $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,11,$FrameStart - 21) ;read up to old frame EndIf If $bNewFrameData <> -1 Then $ID3v2_RawDataBinary_Temp &= $bNewFrameData ;read in new Frame EndIf ;~ MsgBox(0,"Compare",$ID3v2_RawDataBinary_Temp & @CRLF & @CRLF & BinaryMid($ID3v2_RawDataBinary,1,$FrameStart + $iOldFrameSize - 1)) $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,$FrameStart + $FrameSize) ;read in rest of TAG Else Local $FrameStart = $iOldTagSize + 10 + 1 Local $iFrameInfo = StringInStr($ID3v2_TagFrameString,"ZPAD") If $iFrameInfo <> 0 Then ;Add New to end of frame before ZPAD Local $aFrameIDSplit = StringSplit($ID3v2_TagFrameString, "ZPAD", 1) Local $iNumFrameIDs = $aFrameIDSplit[0] - 1 Local $FrameInfoCut = StringMid($ID3v2_TagFrameString,$iFrameInfo) Local $iFrameInfoEnd = StringInStr($FrameInfoCut,@CRLF) $FrameInfoCut = StringMid($FrameInfoCut,1,$iFrameInfoEnd-1) $Firstpipe = StringInStr($FrameInfoCut,'|') $Lastpipe = StringInStr($FrameInfoCut,'|',-1,-1) $FrameStart = Number(StringMid($FrameInfoCut,$Firstpipe+1,($Lastpipe)-($Firstpipe+1))) EndIf $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,11,$FrameStart - 10 -1) ;read up to ZPAD $ID3v2_RawDataBinary_Temp &= $bNewFrameData ;read in new Frame $ID3v2_RawDataBinary_Temp &= BinaryMid($ID3v2_RawDataBinary,$FrameStart) EndIf ;**************************************************************************************** $ID3v2_RawDataBinary = $ID3v2_RawDataBinary_Temp _h_ID3v2Tag_EnumerateFrameIDs() EndFunc Func _ID3v2Frame_GetFields($sFrameID,$iFrameID_Index = 1,$iReturnTypeFlag = 0) ;$iReturnTypeFlag ;0 (Default) => Returns single text string that mostly describes the frame ;1 => Returns Array of all items in frame ;Will take advantage of the variant data type in AutoIt ;20120327 change this function to return an array with all frame fields including FrameID Local $vFrameString = "", $iSetError = 0 ;If version = ID3v2.2 then convert FrameID Local $iTagVersion = Number(StringLeft(_ID3v2Tag_GetVersion(),1)) If $iTagVersion == 2 Then _h_ID3v2_ConvertFrameID($sFrameID) EndIf Local $bFrameData = _ID3v2Frame_GetBinary($sFrameID,$iFrameID_Index) Local $iNumFrameIDs = @extended ;Check if FrameID was not found If $bFrameData == -1 Then $iSetError = 1 If $iReturnTypeFlag == 1 Then Local $vFrameString[6] ;this will avoid errors if an array is expected EndIf SetError($iSetError) SetExtended($iNumFrameIDs) Return $vFrameString EndIf If (StringMid($sFrameID,1,1) == "T") and ($sFrameID <> "TXXX") and ($sFrameID <> "TXX") Then ;Information | TextEncoding $vFrameString = _h_ID3v2_GetFrameT000_TZZZ($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] If ($sFrameID == "TCON") or ($sFrameID == "TCO") Then;Content Type/Genre If StringMid($vFrameString,1,1) == "(" Then ;check if first char is "(" $closeparindex = StringInStr($vFrameString,")") $GenreID = StringMid($vFrameString,2,$closeparindex-1) $vFrameString = _h_ID3v1_GetGenreFromID($GenreID) EndIf ;If no "(" then return the whole field as is EndIf EndIf ElseIf (StringMid($sFrameID,1,1) == "W") and ($sFrameID <> "WXXX") and ($sFrameID <> "WXX") Then $vFrameString = _h_ID3v2_GetFrameW000_WZZZ($bFrameData) Else Switch $sFrameID Case "TXXX", "TXX" ;User defined text information frame ;Value | Description | TextEncoding $vFrameString = _h_ID3v2_GetFrameTXXX($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;Value EndIf Case "WXXX", "WXX" ;User defined URL link frame ;URL | Description | TextEncoding $vFrameString = _h_ID3v2_GetFrameWXXX($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;URL EndIf Case "COMM", "COM" ;Comment ;CommentText | Description | Language | TextEncoding $vFrameString = _h_ID3v2_GetFrameCOMM($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;CommentText EndIf Case "APIC", "PIC" ;Attached picture ;PictureFileName | Description | PictureType | MIMEType | TextEncoding $vFrameString = _h_ID3v2_GetFrameAPIC($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] & Chr(0) & Number($vFrameString[3]);PictureFileName & chr(0) & PictureType EndIf Case "USLT", "ULT" ;Unsychronized lyric/text transcription ;LyricsFilename | Description | Language | TextEncoding $vFrameString = _h_ID3v2_GetFrameUSLT($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;LyricsFilename EndIf Case "UFID", "UFI" ;Unique file identifier ;OwnerIdentifier | Identifier $vFrameString = _h_ID3v2_GetFrameUFID($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;OwnerIdentifier EndIf Case "POPM", "POP" ;Popularimeter ;Rating | EmailToUser | Counter $vFrameString = _h_ID3v2_GetFramePOPM($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;Rating EndIf Case "PRIV" ;Private frame ;OwnerIdentifier | PrivateData $vFrameString = _h_ID3v2_GetFramePRIV($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;OwnerIdentifier EndIf Case "PCNT", "CNT" ;Play counter $vFrameString = _h_ID3v2_GetFramePCNT($bFrameData) ;Counter Case "MCDI", "MCI" ;Music CD identifier (Only contains binary data) $vFrameString = $bFrameData Case "RGAD" ;Replay Gain Adjustment ;PeakAmplitude | RadioReplayGainAdj | AudiophileReplayGainAdj $vFrameString = _h_ID3v2_GetFrameRGAD($bFrameData) If $iReturnTypeFlag == 0 Then $vFrameString = $vFrameString[1] ;PeakAmplitude EndIf Case Else $iSetError = 2 $vFrameString = $bFrameData EndSwitch EndIf SetError($iSetError) SetExtended($iNumFrameIDs) Return $vFrameString EndFunc Func _ID3v2Frame_SetFields($sFrameID,$vNewFrameStrings,$iFrameID_Index = 1,$sDelimiter = -1) ;If $vNewFrameStrings is an empty string of length=0 then frame will be removed ;TODO if ID3v2 does not exist need to create header ;$sDelimiter ;-1 (Default) => If IsString($vNewFrameStrings) == True then $vNewFrameStrings is a simple text string that mostly describes the frame ;If IsArray($vNewFrameStrings) == True then $vNewFrameStrings is an array of simple text strings for each field of frame in proper order ;" " => any string to be used as a delimiter for $vNewFrameStrings to contain main text fields of frame in proper order ;Check if tag does not exist Local $sID3v2_TagID = BinaryToString(BinaryMid($ID3v2_RawDataBinary,1,3)) If (StringCompare($sID3v2_TagID,"ID3") <> 0) Then _ID3v2Tag_CreateTag(4) EndIf Local $bFrameData If (StringMid($sFrameID,1,1) == "T") and (StringLen($sFrameID) == 4) and ($sFrameID <> "TXXX") Then ;_h_ID3v2_CreateFrameT000_TZZZ($sInformation, $iTextEncoding = 0) If IsString($sDelimiter) Then ;Complex Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameT000_TZZZ($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameT000_TZZZ($aNewFrameStrings[1],Number($aNewFrameStrings[2])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameT000_TZZZ($vNewFrameStrings[1],Number($vNewFrameStrings[2])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameT000_TZZZ($vNewFrameStrings) EndIf EndIf Elseif $sFrameID == "TXXX" Then ;_h_ID3v2_CreateFrameTXXX($sValue,$sDescription = "",$iTextEncoding = 0) If IsString($sDelimiter) Then ;Complex Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameTXXX($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameTXXX($aNewFrameStrings[2],$aNewFrameStrings[1]) Case 3 $bFrameData = _h_ID3v2_CreateFrameTXXX($aNewFrameStrings[2],$aNewFrameStrings[1],Number($aNewFrameStrings[3])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData =_h_ID3v2_CreateFrameTXXX($vNewFrameStrings[1],$vNewFrameStrings[2],Number($vNewFrameStrings[3])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameTXXX($vNewFrameStrings) EndIf EndIf ElseIf (StringMid($sFrameID,1,1) == "W") and (StringLen($sFrameID) == 4) and ($sFrameID <> "WXXX") Then ;_h_ID3v2_CreateFrameW000_WZZZ($sURL) $bFrameData = _h_ID3v2_CreateFrameW000_WZZZ($vNewFrameStrings) ElseIf $sFrameID == "WXXX" Then ;_h_ID3v2_CreateFrameWXXX($sURL,$sDescription = "",$iTextEncoding = 0) If IsString($sDelimiter) Then ;Complex Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameWXXX($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameWXXX($aNewFrameStrings[1],$aNewFrameStrings[2]) Case 3 $bFrameData = _h_ID3v2_CreateFrameWXXX($aNewFrameStrings[1],$aNewFrameStrings[2],Number($aNewFrameStrings[3])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameWXXX($vNewFrameStrings[1],$vNewFrameStrings[2],Number($vNewFrameStrings[3])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameWXXX($vNewFrameStrings) EndIf EndIf Else Switch $sFrameID Case "COMM", "COM" ;Comment ;_h_ID3v2_CreateFrameCOMM($sText,$sDescription = "",$sLanguage = "eng",$iTextEncoding = 0) If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameCOMM($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameCOMM($aNewFrameStrings[1],$aNewFrameStrings[2]) Case 3 $bFrameData = _h_ID3v2_CreateFrameCOMM($aNewFrameStrings[1],$aNewFrameStrings[2],$aNewFrameStrings[3]) Case 4 $bFrameData = _h_ID3v2_CreateFrameCOMM($aNewFrameStrings[1],$aNewFrameStrings[2],$aNewFrameStrings[3],Number($aNewFrameStrings[4])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameCOMM($vNewFrameStrings[1],$vNewFrameStrings[2],$vNewFrameStrings[3],Number($vNewFrameStrings[4])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameCOMM($vNewFrameStrings) EndIf EndIf Case "APIC" ;_h_ID3v2_CreateFrameAPIC($sPictureFilename,$sDescription = "",$iPictureType = 0,$sMIMEType = -1,$iTextEncoding = 0) If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameAPIC($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameAPIC($aNewFrameStrings[1],$aNewFrameStrings[2]) Case 3 $bFrameData = _h_ID3v2_CreateFrameAPIC($aNewFrameStrings[1],$aNewFrameStrings[2],Number($aNewFrameStrings[3])) Case 4 $bFrameData = _h_ID3v2_CreateFrameAPIC($aNewFrameStrings[1],$aNewFrameStrings[2],Number($aNewFrameStrings[3]),$aNewFrameStrings[4]) Case 5 $bFrameData = _h_ID3v2_CreateFrameAPIC($aNewFrameStrings[1],$aNewFrameStrings[2],Number($aNewFrameStrings[3]),$aNewFrameStrings[4],Number($aNewFrameStrings[5])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameAPIC($vNewFrameStrings[1],$vNewFrameStrings[2],Number($vNewFrameStrings[3]),$vNewFrameStrings[4],Number($vNewFrameStrings[5])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameAPIC($vNewFrameStrings) EndIf EndIf Case "USLT" ;_h_ID3v2_CreateFrameUSLT($sLyricsFilename,$sDescription = "",$sLanguage = "eng",$iTextEncoding = 0) If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameUSLT($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameUSLT($aNewFrameStrings[1],$aNewFrameStrings[2]) Case 3 $bFrameData = _h_ID3v2_CreateFrameUSLT($aNewFrameStrings[1],$aNewFrameStrings[2],$aNewFrameStrings[3]) Case 4 $bFrameData = _h_ID3v2_CreateFrameUSLT($aNewFrameStrings[1],$aNewFrameStrings[2],$aNewFrameStrings[3],Number($aNewFrameStrings[4])) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUSLT($vNewFrameStrings[1],$vNewFrameStrings[2],$vNewFrameStrings[3],Number($vNewFrameStrings[4])) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUSLT($vNewFrameStrings) EndIf EndIf Case "PCNT" ;_h_ID3v2_CreateFramePCNT($iCounter = 0) ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFramePCNT(Number($vNewFrameStrings)) Else ;$vNewFrameStrings is an integer $bFrameData = _h_ID3v2_CreateFramePCNT($vNewFrameStrings) EndIf Case "UFID" ;_h_ID3v2_CreateFrameUFID($bIdentifier, $sOwnerIdentifier = "http://www.id3.org/dummy/ufid.html") If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameUFID($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameUFID($aNewFrameStrings[1],$aNewFrameStrings[2]) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUFID($vNewFrameStrings[1],$vNewFrameStrings[2]) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUFID(0,$vNewFrameStrings) EndIf If IsBinary($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUFID($vNewFrameStrings) EndIf EndIf Case "POPM" ;Popularimeter ;_h_ID3v2_CreateFramePOPM($bRating,$sEmailToUser = "",$bCounter = 0) If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFramePOPM($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFramePOPM($aNewFrameStrings[1],$aNewFrameStrings[2]) Case 3 $bFrameData = _h_ID3v2_CreateFramePOPM($aNewFrameStrings[1],$aNewFrameStrings[2],$aNewFrameStrings[3]) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFramePOPM($vNewFrameStrings[1],$vNewFrameStrings[2],$vNewFrameStrings[3]) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFramePOPM(0,$vNewFrameStrings) EndIf If IsBinary($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFramePOPM($vNewFrameStrings) EndIf EndIf Case "MCDI" ;contains only binary data If IsBinary($vNewFrameStrings) Then $bFrameData = $vNewFrameStrings Else $bFrameData = Binary($vNewFrameStrings) EndIf Case "PRIV" ;_h_ID3v2_CreateFramePRIV($sOwnerIdentifier,$bPrivateData = 0) If IsString($sDelimiter) Then ;Delimited String Local $aNewFrameStrings = StringSplit($vNewFrameStrings,$sDelimiter,1) Switch $aNewFrameStrings[0] Case 1 $bFrameData = _h_ID3v2_CreateFrameUFID($aNewFrameStrings[1]) Case 2 $bFrameData = _h_ID3v2_CreateFrameUFID($aNewFrameStrings[1],$aNewFrameStrings[2]) Case Else ;Too many EndSwitch Else ;Array If IsArray($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUFID($vNewFrameStrings[1],$vNewFrameStrings[2]) EndIf ;Simple Text String If IsString($vNewFrameStrings) Then $bFrameData = _h_ID3v2_CreateFrameUFID($vNewFrameStrings) EndIf EndIf Case Else MsgBox(0,"_ID3v2Frame_SetFields Error",$sFrameID & " has not been implemented yet!") SetError(1) Return -1 EndSwitch EndIf Local $NewFrameSize = BinaryLen($bFrameData) Local $iID3v2_Version = _ID3v2Tag_GetVersion() Local $bFrameSize = Binary("0x" & Hex($NewFrameSize,8)) If StringInStr($iID3v2_Version,"4.") Then ;ID3v2.4.X ;SyncSafe Integer ;4444 4444 3333 3333 2222 2222 1111 1111 ;0444 4333 0333 3322 0222 2221 0111 1111 Local $byte1 = BitAND(BinaryMid($bFrameSize,4,1),127) Local $byte2 = BitAND(BitShift(BinaryMid($bFrameSize,3,1),-1) + BitShift(BinaryMid($bFrameSize,4,1),7),127) Local $byte3 = BitAND(BitShift(BinaryMid($bFrameSize,2,1),-2) + BitShift(BinaryMid($bFrameSize,3,1),6),127) Local $byte4 = BitAND(BitShift(BinaryMid($bFrameSize,1,1),-3) + BitShift(BinaryMid($bFrameSize,2,1),5),127) Local $iSyncSafeFrameSize = BitShift($byte4,-24) + BitShift($byte3,-16) + BitShift($byte2,-8) + $byte1 Local $bSyncSafeFrameSize = Binary("0x" & String(Hex($iSyncSafeFrameSize,8))) $bFrameSize = $bSyncSafeFrameSize Else ;~ MsgBox(0,"Check Tag Version",$iID3v2_Version) ;Test Code EndIf Local $bNewFrameWithHeader = Binary($sFrameID) & $bFrameSize & Binary("0x00") & Binary("0x00") & $bFrameData ;~ MsgBox(0,"$bNewFrameWithHeader",$bNewFrameWithHeader) If (Not IsArray($vNewFrameStrings)) and (StringLen($vNewFrameStrings) == 0) Then _ID3v2Frame_SetBinary($sFrameID,-1,$iFrameID_Index) ;Remove Frame Else _ID3v2Frame_SetBinary($sFrameID,$bNewFrameWithHeader,$iFrameID_Index) EndIf EndFunc Func _h_ID3v2FrameHeader_GetFrameID($bFrameHeaderData) Local $iFrameIDLen = 4, $bFrameID If StringInStr(_ID3v2Tag_GetVersion(),"2.") Then $iFrameIDLen = 3 Else $iFrameIDLen = 4 EndIf $bFrameID = BinaryMid($bFrameHeaderData,1,$iFrameIDLen) Local $iASC = 0 For $i = 1 To $iFrameIDLen $iASC = Asc(BinaryToString(BinaryMid($bFrameID,$i))) If $i == 1 Then If ($iASC < Asc("A")) Or ($iASC > Asc("Z")) Then Return -1 EndIf Else If ($iASC < Asc("0")) Or ($iASC > Asc("9")) Then If ($iASC < Asc("A")) Or ($iASC > Asc("Z")) Then Return -1 EndIf EndIf EndIf Next ;Should check against exsisting FRAMEIDs - this will help fix bad/corrupt tags Return BinaryToString($bFrameID) EndFunc Func _h_ID3v2FrameHeader_GetFrameSize($bFrameHeaderData) ;(ID3v2.2) Frame ID $xx xx xx (three characters) ;Size $xx xx xx ;The three character frame identifier is followed by a three byte size ;descriptor, making a total header size of six bytes in every frame. ;The size is calculated as framesize excluding frame identifier and ;size descriptor (frame size - 6). ;(ID3v2.3) Frame ID $xx xx xx xx (four characters) ;Size $xx xx xx xx ;Flags $xx xx ;The frame ID is followed by a size descriptor, making a total header ;size of ten bytes in every frame. The size is calculated as frame ;size excluding frame header (frame size - 10). ;(ID3v2.4) Frame ID $xx xx xx xx (four characters) ;Size 4 * %0xxxxxxx ;Flags $xx xx ;The frame ID is followed by a size descriptor containing the size of ;the data in the final frame, after encryption, compression and ;unsynchronisation. The size is excluding the frame header ('total ;frame size' - 10 bytes) and stored as a 32 bit synchsafe integer. Local $bFrameSize, $iFrameSize = 0 Local $iID3v2_Version = _ID3v2Tag_GetVersion() If StringInStr($iID3v2_Version,"2.") Then ;ID3v2.2.X $bFrameSize = BinaryMid($bFrameHeaderData,4,3) $iFrameSize = Dec(Hex($bFrameSize)) ;~ MsgBox(0,"$bFrameHeaderData",$bFrameHeaderData) ;Test Code ;~ MsgBox(0,"$iFrameSize",$iFrameSize) ;Test Code ElseIf StringInStr($iID3v2_Version,"3.") Then ;ID3v2.3.X $bFrameSize = BinaryMid($bFrameHeaderData,5,4) $iFrameSize = Dec(Hex($bFrameSize),2) ElseIf StringInStr($iID3v2_Version,"4.") Then ;ID3v2.4.X $bFrameSize = BinaryMid($bFrameHeaderData,5,4) ;SyncSafe Integer ;0444 4333 0333 3322 0222 2221 0111 1111 ;0000 4444 3333 3333 2222 2222 1111 1111 (28 bits) Local $byte1 = BitAND(BinaryMid($bFrameSize,1,1),127) Local $byte2 = BitAND(BinaryMid($bFrameSize,2,1),127) Local $byte3 = BitAND(BinaryMid($bFrameSize,3,1),127) Local $byte4 = BitAND(BinaryMid($bFrameSize,4,1),127) $bFrameSize = BitShift($byte1,-21) + BitShift($byte2,-14) + BitShift($byte3,-7) + $byte4 $iFrameSize = Dec(Hex($bFrameSize),2) Else ;~ MsgBox(0,"Check Tag Version",$iID3v2_Version) ;Test Code EndIf Return $iFrameSize EndFunc Func _h_ID3v2FrameHeader_GetFlags($bFrameHeaderData, $sFlagReturnType = -1) ;ID3v2.4 ;Frame ID $xx xx xx xx (four characters) ;Size 4 * %0xxxxxxx ;Flags $xx xx ;ID3v2_FrameHeaderFlags = %0abc0000 %0h00kmnp ;a - Tag alter preservation ;b - File alter preservation ;c - Read only ;h - Grouping identity ;k - Compression ;m - Encryption ;n - Unsynchronisation ;p - Data length indicator Local $bFrameFlags = BinaryMid($bFrameHeaderData,9,2) If $sFlagReturnType == "RawBinary" Then Return $bFrameFlags EndIf Local $bFrameFlags_MSB = BinaryMid($bFrameHeaderData,9,1) Local $bFrameFlags_LSB = BinaryMid($bFrameHeaderData,10,1) Local $TagAlterPreservation = BitShift(BitAND($bFrameFlags_MSB,64),6) Local $FileAlterPreservation = BitShift(BitAND($bFrameFlags_MSB,32),5) Local $ReadOnly = BitShift(BitAND($bFrameFlags_MSB,16),4) Local $GroupingIdentity = BitShift(BitAND($bFrameFlags_LSB,64),6) Local $Compression = BitShift(BitAND($bFrameFlags_LSB,8),3) Local $Encryption = BitShift(BitAND($bFrameFlags_LSB,4),2) Local $Unsynchronisation = BitShift(BitAND($bFrameFlags_LSB,2),1) Local $DataLengthIndicator = BitShift(BitAND($bFrameFlags_LSB,1),0) If $sFlagReturnType == -1 Then Return "TagAlterPreservation" & "|" & $TagAlterPreservation & @CRLF & _ "FileAlterPreservation" & "|" & $FileAlterPreservation & @CRLF & _ "$ReadOnly" & "|" & $ReadOnly & @CRLF & _ "GroupingIdentity" & "|" & $GroupingIdentity & @CRLF & _ "Compression" & "|" & $Compression & @CRLF & _ "Encryption" & "|" & $Encryption & @CRLF & _ "Unsynchronisation" & "|" & $Unsynchronisation & @CRLF & _ "DataLengthIndicator" & "|" & $DataLengthIndicator EndIf Switch $sFlagReturnType Case "TagAlterPreservation" Return $TagAlterPreservation Case "FileAlterPreservation" Return $FileAlterPreservation Case "$ReadOnly" Return $ReadOnly Case "GroupingIdentity" Return $GroupingIdentity Case "Compression" Return $Compression Case "Encryption" Return $Encryption Case "Unsynchronisation" Return $Unsynchronisation Case "DataLengthIndicator" Return $DataLengthIndicator Case Else SetError(1) Return $bFrameFlags EndSwitch EndFunc Func _h_ID3v2_GetFrameT000_TZZZ(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Text Encoding $xx ;Information ;ID3v2.2 ;Text information identifier "T00" - "TZZ" , excluding "TXX" ;Text encoding $xx ;Information ;--------------------------------------------------------------------------------- ;Information | TextEncoding Local $aFrameInfo[3] $aFrameInfo[0] = 2 $aFrameInfo[1] = _h_ID3v2_DecodeTextToString(BinaryMid($bFrameData,1,1),BinaryMid($bFrameData,2)) ;Information $aFrameInfo[2] = "0x" & Hex(BinaryMid($bFrameData,1,1),2) ;Text Encoding Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameT000_TZZZ($sInformation, $iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text Encoding $xx ;Information ;--------------------------------------------------------------------------------- Local $bFrameData = Binary("0x0" & String($iTextEncoding)) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sInformation) Return $bFrameData EndFunc Func _h_ID3v2_GetFrameTXXX(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Description $00 (00) ;Value ;--------------------------------------------------------------------------------- ;Value | Description | TextEncoding Local $bText_Encoding, $sDescription, $sValue, $iDescriptionEndIndex Local $aFrameInfo[4] $aFrameInfo[0] = 3 $bText_Encoding = BinaryMid($bFrameData,1,1) $iDescriptionEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,2)),chr(0)) $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,2,$iDescriptionEndIndex-1)) $sValue = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,$iDescriptionEndIndex+2)) $aFrameInfo[1] = $sValue $aFrameInfo[2] = $sDescription $aFrameInfo[3] = "0x" & Hex($bText_Encoding,2) Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameTXXX($sValue,$sDescription = "",$iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Description $00 (00) ;Value ;--------------------------------------------------------------------------------- Local $bFrameData = Binary("0x0" & String($iTextEncoding)) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sDescription) & Binary("0x00") $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sValue) Return $bFrameData EndFunc Func _h_ID3v2_GetFrameW000_WZZZ(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;URL ;If nothing else is said, strings, including numeric strings and URLs ;[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the ;range $20 - $FF. ;--------------------------------------------------------------------------------- Local $bText_Encoding = Binary(Chr(0)) $sFrameString = _h_ID3v2_DecodeTextToString($bText_Encoding,$bFrameData) Return $sFrameString EndFunc Func _h_ID3v2_CreateFrameW000_WZZZ($sURL) ;--------------------------------------------------------------------------------- ;
;URL ;If nothing else is said, strings, including numeric strings and URLs ;[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the ;range $20 - $FF. ;--------------------------------------------------------------------------------- Local $bFrameData $bFrameData = _h_ID3v2_EncodeStringToBinary(0,$sURL) Return $bFrameData EndFunc Func _h_ID3v2_GetFrameWXXX(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Description $00 (00) ;URL ;--------------------------------------------------------------------------------- ;URL | Description | TextEncoding Local $bText_Encoding, $sDescription, $sURL, $iDescriptionEndIndex Local $aFrameInfo[4] $aFrameInfo[0] = 3 $bText_Encoding = BinaryMid($bFrameData,1,1) $iDescriptionEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,2)),chr(0)) $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,2,$iDescriptionEndIndex-1)) $sURL = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,2+$iDescriptionEndIndex)) $aFrameInfo[1] = $sURL $aFrameInfo[2] = $sDescription $aFrameInfo[3] = "0x" & Hex($bText_Encoding,2) Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameWXXX($sURL,$sDescription = "",$iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Description $00 (00) ;URL ;--------------------------------------------------------------------------------- Local $bFrameData = Binary("0x0" & String($iTextEncoding)) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sDescription) & Binary("0x00") $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sURL) Return $bFrameData EndFunc Func _h_ID3v2_GetFrameCOMM(ByRef $bFrameData) ;Newline characters are allowed in the comment text string. There may be more than one ;comment frame in each tag, but only one with the same language and content descriptor. ;Many of my files have c0 as content descrip. MP3TagPro reads content descrip. as comment text ;MP3Tag reads content desrip. as part of FrameID (Comment C0) but adds more comment with same content descrip. ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Language $xx xx xx ;Short content descrip. $00 (00) ;The actual text "eng" Then ;MsgBox(0,"Text may not be in English",$Language) ;text may not be in English EndIf ;Find the $00 at the end of Short content descrip. $iDescriptionEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,5)),chr(0)) ;Decode the Text $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,5,$iDescriptionEndIndex-4)) $sCommentText = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,5+$iDescriptionEndIndex)) $aFrameInfo[1] = $sCommentText $aFrameInfo[2] = $sDescription $aFrameInfo[3] = $sLanguage $aFrameInfo[4] = "0x" & Hex($bText_Encoding,2) Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameCOMM($sText,$sDescription = "",$sLanguage = "eng",$iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Language $xx xx xx ;Short content descrip. $00 (00) ;The actual text ;Text encoding $xx ;MIME type $00 ;Picture type $xx ;Description $00 (00) ;Picture data ;ID3v2.2
;Text encoding $xx ;Image format $xx xx xx {Image format is preferably "PNG" [PNG] or "JPG" [JFIF]} ;Picture type $xx ;Description $00 (00) ;Picture data ;--------------------------------------------------------------------------------- ;PictureFileName | Description | PictureType | MIMEType | TextEncoding Local $sPictureFileName, $sDescription, $iPictureType, $sMIMEType, $bText_Encoding, $iMIMETypeEndIndex, $iDescriptionEndIndex, $iBinaryStartIndex Local $aFrameInfo[6] $aFrameInfo[0] = 5 $sPictureFileName = @ScriptDir & "\" & "AlbumArt" $ID3Filenames &= $sPictureFileName & "|" ;~ MsgBox(0,"APIC",BinaryMid($bFrameData,1,32)) $bText_Encoding = BinaryMid($bFrameData,1,1) ;added this to handle ID3v2.2 PIC frame Local $iID3v2_Version = _ID3v2Tag_GetVersion() If StringInStr($iID3v2_Version,"2.") Then ;ID3v2.2.X $iMIMETypeEndIndex = 3 $sMIMEType = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,2,3)) ;~ MsgBox(0,"$sMIMEType",$sMIMEType) Else $iMIMETypeEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,2)),chr(0)) $sMIMEType = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,2,$iMIMETypeEndIndex-1)) ;Added this because Foobar2000 adds a field 0x01246600, not sure what this is for ;Should these bytes be removed?? so that other software can read this APIC Frame? If StringInStr($sMIMEType,"image") == 0 Then Local $iMIMETypeBeginIndex = $iMIMETypeEndIndex+2 ;find the next occurance of 0x00 $iMIMETypeEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,2)),chr(0),0,2) $sMIMEType = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,$iMIMETypeBeginIndex,$iMIMETypeEndIndex-$iMIMETypeBeginIndex + 1)) EndIf ;~ MsgBox(0,"$sMIMEType",$sMIMEType) EndIf $iPictureType = BinaryMid($bFrameData,$iMIMETypeEndIndex+2,1) $iDescriptionEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,$iMIMETypeEndIndex+3)),chr(0)) $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,$iMIMETypeEndIndex+3,$iDescriptionEndIndex-1)) $iBinaryStartIndex = $iMIMETypeEndIndex + $iDescriptionEndIndex + 3 ;Check $iBinaryStartIndex shows the 0xFF 0xD8 for Start of image for JPEG SOI Segment If StringInStr($sMIMEType,"jpg") Or StringInStr($sMIMEType,"jpeg") Then Local $iBinaryStartIndex_Test = $iBinaryStartIndex, $SOI_NotFound = False While BinaryMid($bFrameData,$iBinaryStartIndex_Test,2) <> Binary("0xFFD8") $iBinaryStartIndex_Test += 1 If BinaryLen(BinaryMid($bFrameData,$iBinaryStartIndex_Test)) < 10 Then $SOI_NotFound = True ExitLoop EndIf WEnd If $SOI_NotFound = False Then If $iBinaryStartIndex <> $iBinaryStartIndex_Test Then $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,$iMIMETypeEndIndex+3,($iBinaryStartIndex_Test-$iMIMETypeEndIndex-3)-1)) EndIf $iBinaryStartIndex = $iBinaryStartIndex_Test EndIf EndIf If StringInStr($sMIMEType,"jpg") Or StringInStr($sMIMEType,"jpeg") Then $sPictureFileName &= ".jpg" $ID3Filenames &= $sPictureFileName & "|" ElseIf StringInStr($sMIMEType,"png") Then $sPictureFileName &= ".png" $ID3Filenames &= $sPictureFileName & "|" Else $sPictureFileName = "File Type Unknown" EndIf ;Read Picture data to file ;**************************************************************************************** Local $PicFile_h = FileOpen($sPictureFileName, 2) ;Open for write and erase existing $WriteError = FileWrite($PicFile_h,BinaryMid($bFrameData,$iBinaryStartIndex)) FileClose($PicFile_h) ;**************************************************************************************** $aFrameInfo[1] = $sPictureFileName $aFrameInfo[2] = $sDescription $aFrameInfo[3] = $iPictureType $aFrameInfo[4] = $sMIMEType $aFrameInfo[5] = "0x" & Hex($bText_Encoding,2) ;~ _ArrayDisplay($aFrameInfo) Return $aFrameInfo;$sAlbumArtFilename & Chr(0) & Number($bPicture_Type) EndFunc Func _h_ID3v2_CreateFrameAPIC($sPictureFilename,$sDescription = "",$iPictureType = 0,$sMIMEType = -1,$iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;MIME type $00 ;Picture type $xx ;Description $00 (00) ;Picture 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. lable 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 Local $bFrameData = Binary("0x0" & String($iTextEncoding)) If $sMIMEType == -1 Then Local $szDrive, $szDir, $szFName, $szExt _PathSplit($sPictureFilename, $szDrive, $szDir, $szFName, $szExt) If StringInStr($szExt,"jpg") Or StringInStr($szExt,"jpeg") Then $sMIMEType = "image/jpeg" ElseIf StringInStr($szExt,"png") Then $sMIMEType = "image/png" Else $sMIMEType = "" EndIf EndIf $bFrameData &= _h_ID3v2_EncodeStringToBinary(0,$sMIMEType) & Binary("0x00") $bFrameData &= Binary("0x" & Hex($iPictureType, 2)) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sDescription) & Binary("0x00") $PicFile_h = FileOpen($sPictureFilename, 16) ;force binary $bFrameData &= FileRead($PicFile_h) FileClose($PicFile_h) Return $bFrameData EndFunc Func _h_ID3v2_GetFrameUSLT(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Language $xx xx xx ;Content descriptor $00 (00) ;Lyrics/text ;--------------------------------------------------------------------------------- ;LyricsFilename | Description | Language | TextEncoding Local $bText_Encoding, $sDescription, $sLanguage, $sLyricsFilename, $hLyricFile, $sLyricsText, $iDescriptionEndIndex Local $aFrameInfo[5] $aFrameInfo[0] = 4 $sLyricsFilename = @ScriptDir & "\" & "SongLyrics.txt" $ID3Filenames &= $sLyricsFilename & "|" $bText_Encoding = BinaryMid($bFrameData,1,1) $sLanguage = BinaryToString(BinaryMid($bFrameData,2,3)) $iDescriptionEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,5)),chr(0)) $sDescription = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,5,$iDescriptionEndIndex-1)) $sLyricsText = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,$iDescriptionEndIndex + 5)) $hLyricFile = FileOpen($sLyricsFilename, 2) ;Open for write and erase existing FileWrite($hLyricFile,$sLyricsText) FileClose($hLyricFile) $aFrameInfo[1] = $sLyricsFilename $aFrameInfo[2] = $sDescription $aFrameInfo[3] = $sLanguage $aFrameInfo[4] = "0x" & Hex($bText_Encoding,2) Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameUSLT($sLyricsFilename,$sDescription = "",$sLanguage = "eng",$iTextEncoding = 0) ;--------------------------------------------------------------------------------- ;
;Text encoding $xx ;Language $xx xx xx ;Content descriptor $00 (00) ;Lyrics/text ;--------------------------------------------------------------------------------- Local $sLyrics = "" If FileExists($sLyricsFilename) Then $sLyrics = FileRead($sLyricsFilename) EndIf Local $bFrameData = Binary("0x0" & String($iTextEncoding)) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sLanguage) $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sDescription) & Binary("0x00") $bFrameData &= _h_ID3v2_EncodeStringToBinary($iTextEncoding,$sLyrics) ;~ MsgBox(0,"$bFrameData",$bFrameData) Return $bFrameData EndFunc Func _h_ID3v2_GetFramePCNT(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Counter $xx xx xx xx (xx ...) ;This is simply a counter of the number of times a file has been ;played. The value is increased by one every time the file begins to ;play. There may only be one "PCNT" frame in each tag. When the ;counter reaches all one's, one byte is inserted in front of the ;counter thus making the counter eight bits bigger. The counter must ;be at least 32-bits long to begin with. ;--------------------------------------------------------------------------------- ;Counter Local $iCounter = Dec(Hex(BinaryMid($bFrameData,1))) ;Hex function can only work up to 16 bytes ;Dec will only work up to 64 bit signed integer Return $iCounter EndFunc Func _h_ID3v2_CreateFramePCNT($iCounter = 0) ;--------------------------------------------------------------------------------- ;
;Counter $xx xx xx xx (xx ...) ;This is simply a counter of the number of times a file has been ;played. The value is increased by one every time the file begins to ;play. There may only be one "PCNT" frame in each tag. When the ;counter reaches all one's, one byte is inserted in front of the ;counter thus making the counter eight bits bigger. The counter must ;be at least 32-bits long to begin with. ;--------------------------------------------------------------------------------- Local $bFrameData = Binary("0x" & Hex($iCounter)) ;Hex function can only work up to 16 bytes Return $bFrameData EndFunc Func _h_ID3v2_GetFrameUFID(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Owner identifier $00 ;Identifier ;This frame's purpose is to be able to identify the audio file in a database that may contain ;more information relevant to the content. Since standardisation of such a database is beyond ;this document, all frames begin with a null-terminated string with a URL [URL] containing an ;email address, or a link to a location where an email address can be found, that belongs to ;the organisation responsible for this specific database implementation. Questions regarding the ;database should be sent to the indicated email address. The URL should not be used for the actual ;database queries. The string "http://www.id3.org/dummy/ufid.html" should be used for tests. Software ;that isn't told otherwise may safely remove such frames. The 'Owner identifier' must be non-empty ;(more than just a termination). The 'Owner identifier' is then followed by the actual identifier, ;which may be up to 64 bytes. There may be more than one "UFID" frame in a tag, but only one with ;the same 'Owner identifier'. ;--------------------------------------------------------------------------------- ;OwnerIdentifier | Identifier Local $aFrameInfo[3] $aFrameInfo[0] = 2 Local $bText_Encoding = Binary(Chr(0)) Local $iTextEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,1)),chr(0)) Local $sOwnerIdentifier = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,1,$iTextEndIndex-1)) Local $bIdentifier = BinaryMid($bFrameData,$iTextEndIndex + 1) $aFrameInfo[1] = $sOwnerIdentifier $aFrameInfo[2] = $bIdentifier Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFrameUFID($bIdentifier, $sOwnerIdentifier = "http://www.id3.org/dummy/ufid.html") ;--------------------------------------------------------------------------------- ;
;Owner identifier $00 ;Identifier ;This frame's purpose is to be able to identify the audio file in a database that may contain ;more information relevant to the content. Since standardisation of such a database is beyond ;this document, all frames begin with a null-terminated string with a URL [URL] containing an ;email address, or a link to a location where an email address can be found, that belongs to ;the organisation responsible for this specific database implementation. Questions regarding the ;database should be sent to the indicated email address. The URL should not be used for the actual ;database queries. The string "http://www.id3.org/dummy/ufid.html" should be used for tests. Software ;that isn't told otherwise may safely remove such frames. The 'Owner identifier' must be non-empty ;(more than just a termination). The 'Owner identifier' is then followed by the actual identifier, ;which may be up to 64 bytes. There may be more than one "UFID" frame in a tag, but only one with ;the same 'Owner identifier'. ;--------------------------------------------------------------------------------- Local $bFrameData = _h_ID3v2_EncodeStringToBinary(0,$sOwnerIdentifier) & Binary("0x00") $bFrameData &= $bIdentifier Return $bFrameData EndFunc Func _h_ID3v2_GetFramePOPM(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Email to user $00 ;Rating $xx ;Counter $xx xx xx xx (xx ...) ;If nothing else is said, strings, including numeric strings and URLs ;[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the range $20 - $FF. ;The rating is 1-255 where 1 is worst and 255 is best. 0 is unknown. If no personal counter is ;wanted it may be omitted. When the counter reaches all one's, one byte is inserted ;in front of the counter thus making the counter eight bits bigger. ;--------------------------------------------------------------------------------- ;Rating | EmailToUser | Counter Local $aFrameInfo[4] $aFrameInfo[0] = 3 Local $bText_Encoding = Binary(Chr(0)) Local $iTextEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,1)),chr(0)) Local $sEmailToUser = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,1,$iTextEndIndex-1)) Local $bRating = BinaryMid($bFrameData,$iTextEndIndex + 1,1) Local $bCounter = BinaryMid($bFrameData,$iTextEndIndex + 2) $aFrameInfo[1] = Dec(Hex($bRating)) $aFrameInfo[2] = $sEmailToUser $aFrameInfo[3] = Dec(Hex($bCounter)) Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFramePOPM($bRating,$sEmailToUser = "",$iCounter = 0) ;--------------------------------------------------------------------------------- ;
;Email to user $00 ;Rating $xx ;Counter $xx xx xx xx (xx ...) ;If nothing else is said, strings, including numeric strings and URLs ;[URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the range $20 - $FF. ;The rating is 1-255 where 1 is worst and 255 is best. 0 is unknown. If no personal counter is ;wanted it may be omitted. When the counter reaches all one's, one byte is inserted ;in front of the counter thus making the counter eight bits bigger. ;--------------------------------------------------------------------------------- Local $bFrameData = _h_ID3v2_EncodeStringToBinary(0,$sEmailToUser) & Binary("0x00") $bFrameData &= BinaryMid($bRating,1,1) ;limit to one byte $bFrameData &= Binary("0x" & Hex($iCounter)) ;Hex function can only work up to 16 bytes Return $bFrameData EndFunc Func _h_ID3v2_GetFramePRIV(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Owner identifier $00 ;The private data ;The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email ;address, or a link to a location where an email address can be found, that belongs to the ;organisation responsible for the frame. Questions regarding the frame should be sent to the ;indicated email address. The tag may contain more than one "PRIV" frame but only with different ;contents. It is recommended to keep the number of "PRIV" frames as low as possible. ;--------------------------------------------------------------------------------- ;OwnerIdentifier | PrivateData Local $aFrameInfo[3] $aFrameInfo[0] = 2 Local $bText_Encoding = Binary(Chr(0)) Local $iTextEndIndex = StringInStr(BinaryToString(BinaryMid($bFrameData,1)),chr(0)) Local $sOwnerIdentifier = _h_ID3v2_DecodeTextToString($bText_Encoding,BinaryMid($bFrameData,1,$iTextEndIndex-1)) Local $bPrivateData = BinaryMid($bFrameData,$iTextEndIndex + 1) $aFrameInfo[1] = $sOwnerIdentifier $aFrameInfo[2] = $bPrivateData Return $aFrameInfo EndFunc Func _h_ID3v2_CreateFramePRIV($sOwnerIdentifier,$bPrivateData = 0) ;--------------------------------------------------------------------------------- ;
;Owner identifier $00 ;The private data ;The 'Owner identifier' is a null-terminated string with a URL [URL] containing an email ;address, or a link to a location where an email address can be found, that belongs to the ;organisation responsible for the frame. Questions regarding the frame should be sent to the ;indicated email address. The tag may contain more than one "PRIV" frame but only with different ;contents. It is recommended to keep the number of "PRIV" frames as low as possible. ;--------------------------------------------------------------------------------- Local $bFrameData = _h_ID3v2_EncodeStringToBinary(0,$sOwnerIdentifier) & Binary("0x00") If Not IsBinary($bPrivateData) Then $bPrivateData = Binary($bPrivateData) EndIf $bFrameData &= $bPrivateData Return $bFrameData EndFunc Func _h_ID3v2_GetFrameRGAD(ByRef $bFrameData) ;--------------------------------------------------------------------------------- ;
;Peak Amplitude $xx $xx $xx $xx ;Radio Replay Gain Adjustment $xx $xx ;Audiophile Replay Gain Adjustment $xx $xx ;Header consists of: ;Frame ID $52 $47 $41 $44 = "RGAD" ;Size $00 $00 $00 $08 ;Flags $40 $00 (%01000000 %00000000) ;In the RGAD frame, the flags state that the frame should be preserved if the ID3v2 ;tag is altered, but discarded if the audio data is altered. ;--------------------------------------------------------------------------------- ;PeakAmplitude | RadioReplayGainAdj | AudiophileReplayGainAdj Local $aFrameInfo[4] $aFrameInfo[0] = 3 Local $bPeakAmplitude = BinaryMid($bFrameData,1,4) Local $bRadioReplayGainAdj = BinaryMid($bFrameData,5,2) Local $bAudiophileReplayGainAdj = BinaryMid($bFrameData,7,2) $aFrameInfo[1] = $bPeakAmplitude $aFrameInfo[2] = $bRadioReplayGainAdj $aFrameInfo[3] = $bAudiophileReplayGainAdj Return $aFrameInfo EndFunc Func _h_ID3v2_DecodeTextToString($bText_Encoding_Description_Byte, $bFrameTextBytes) ;From ID3v2.4 TagSpec ;~ If nothing else is said, strings, including numeric strings and URLs ;~ [URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the ;~ range $20 - $FF. Such strings are represented in frame descriptions ;~ as , or if newlines are allowed. If ;~ nothing else is said newline character is forbidden. In ISO-8859-1 a ;~ newline is represented, when allowed, with $0A only. ;~ Frames that allow different types of text encoding contains a text ;~ encoding description byte. Possible encodings: ;~ $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. ;~ $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. ;~ $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. Terminated with $00 00. ;~ $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. ;~ Strings dependent on encoding are represented in frame descriptions ;~ as , or if newlines are allowed. Any empty strings of ;~ type $01 which are NULL-terminated may have the Unicode BOM followed ;~ by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00). Local $BinaryToString_Flag = 1, $bUnicode_BOM, $BinaryToString_Flag, $iStartByte = 2 ;ANSI ;~ 1, Default binary data is taken to be ANSI ;~ 2, binary data is taken to be UTF16 Little Endian (BOM = FF FE) ;~ 3, binary data is taken to be UTF16 Big Endian (BOM = FE FF) ;~ 4, binary data is taken to be UTF8 (BOM = EF BB BF) ;~ NOTE: UTF8 is recommended not to use BOM in which case the default to read as ANSI works fine. Local $iText_Encoding_Description_Byte = Int($bText_Encoding_Description_Byte) Local $sFrameString = "" ;check for NULL at begining of text ;Found in some COMM tags For $ibin = 1 to BinaryLen($bFrameTextBytes) If BinaryToString(BinaryMid($bFrameTextBytes,1,1)) == chr(0) Then $bFrameTextBytes = BinaryMid($bFrameTextBytes,2) Else ExitLoop EndIf Next Switch $iText_Encoding_Description_Byte Case 0 $BinaryToString_Flag = 1 ;ANSI $iStartByte = 1 Case 1 $bUnicode_BOM = BinaryMid($bFrameTextBytes,1,2) If $bUnicode_BOM = "0xFFFE" Then $BinaryToString_Flag = 2 ;UTF16 Little Endian $iStartByte = 3 Else $BinaryToString_Flag = 1 ;ANSI $iStartByte = 1 EndIf Case 2 $bUnicode_BOM = BinaryMid($bFrameTextBytes,1,2) If $bUnicode_BOM = "0xFEFF" Then $BinaryToString_Flag = 3 ;UTF16 Big Endian $iStartByte = 3 Else $BinaryToString_Flag = 1 ;ANSI $iStartByte = 1 EndIf Case 3 $bUnicode_BOM = BinaryMid($bFrameTextBytes,1,3) If StringCompare($bUnicode_BOM,"0xEFBBBF") == 0 Then $BinaryToString_Flag = 3 ;UTF8 $iStartByte = 4 Else $BinaryToString_Flag = 1 ;ANSI $iStartByte = 1 EndIf Case Else $BinaryToString_Flag = 1 ;Assume ANSI EndSwitch $sFrameString = BinaryToString(BinaryMid($bFrameTextBytes,$iStartByte),$BinaryToString_Flag) Return $sFrameString EndFunc Func _h_ID3v2_EncodeStringToBinary($iText_Encoding_Description_Byte, $sFrameText) ;~ Frames that allow different types of text encoding contains a text ;~ encoding description byte. Possible encodings: ;~ $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00. ;~ $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00. ;~ $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. Terminated with $00 00. ;~ $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. ;~ StringToBinary flag description ;~ flag [optional] Changes how the string is stored as binary: ;~ flag = 1 (default), binary data is ANSI ;~ flag = 2, binary data is UTF16 Little Endian ;~ flag = 3, binary data is UTF16 Big Endian ;~ flag = 4, binary data is UTF8 Local $bFrameData Switch $iText_Encoding_Description_Byte Case 0 ;ISO-8859-1 [ISO-8859-1]. Terminated with $00 $bFrameData = StringToBinary($sFrameText,1) ;ANSI Case 1 ;UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM $bFrameData = StringToBinary($sFrameText,2) ;UTF16 Little Endian Case 2 ;UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM $bFrameData = StringToBinary($sFrameText,3) ;UTF16 Big Endian Case 3 ;UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00 $bFrameData = StringToBinary($sFrameText,4) ;UTF8 Case Else $bFrameData = StringToBinary($sFrameText,1) ;ANSI EndSwitch Return $bFrameData EndFunc Func _h_ID3v2_ConvertFrameID(ByRef $sFrameID) $sFrameID = StringReplace($sFrameID,"AENC", "CRA"); Audio encryption $sFrameID = StringReplace($sFrameID,"APIC", "PIC"); Attached picture $sFrameID = StringReplace($sFrameID,"COMM", "COM"); Comments $sFrameID = StringReplace($sFrameID,"EQUA", "EQU"); Equalization $sFrameID = StringReplace($sFrameID,"ETCO", "ETC"); Event timing codes $sFrameID = StringReplace($sFrameID,"GEOB", "GEO"); General encapsulated object $sFrameID = StringReplace($sFrameID,"IPLS", "IPL"); Involved people list $sFrameID = StringReplace($sFrameID,"LINK", "LNK"); Linked information $sFrameID = StringReplace($sFrameID,"MCDI", "MCI"); Music CD identifier $sFrameID = StringReplace($sFrameID,"MLLT", "MLL"); MPEG location lookup table $sFrameID = StringReplace($sFrameID,"PCNT", "CNT"); Play counter $sFrameID = StringReplace($sFrameID,"POPM", "POP"); Popularimeter $sFrameID = StringReplace($sFrameID,"RBUF", "BUF"); Recommended buffer size $sFrameID = StringReplace($sFrameID,"RVAD", "RVA"); Relative volume adjustment $sFrameID = StringReplace($sFrameID,"RVRB", "REV"); Reverb $sFrameID = StringReplace($sFrameID,"SYLT", "SLT"); Synchronized lyric/text $sFrameID = StringReplace($sFrameID,"SYTC", "STC"); Synchronized tempo codes $sFrameID = StringReplace($sFrameID,"TALB", "TAL"); Album/Movie/Show title $sFrameID = StringReplace($sFrameID,"TBPM", "TBP"); BPM (beats per minute) $sFrameID = StringReplace($sFrameID,"TCOM", "TCM"); Composer $sFrameID = StringReplace($sFrameID,"TCON", "TCO"); Content type $sFrameID = StringReplace($sFrameID,"TCOP", "TCR"); Copyright message $sFrameID = StringReplace($sFrameID,"TDAT", "TDA"); Date $sFrameID = StringReplace($sFrameID,"TDLY", "TDY"); Playlist delay $sFrameID = StringReplace($sFrameID,"TENC", "TEN"); Encoded by $sFrameID = StringReplace($sFrameID,"TEXT", "TXT"); Lyricist/Text writer $sFrameID = StringReplace($sFrameID,"TFLT", "TFT"); File type $sFrameID = StringReplace($sFrameID,"TIME", "TIM"); Time $sFrameID = StringReplace($sFrameID,"TIT1", "TT1"); Content group description $sFrameID = StringReplace($sFrameID,"TIT2", "TT2"); Title/songname/content description $sFrameID = StringReplace($sFrameID,"TIT3", "TT3"); Subtitle/Description refinement $sFrameID = StringReplace($sFrameID,"TKEY", "TKE"); Initial key $sFrameID = StringReplace($sFrameID,"TLAN", "TLA"); Language(s) $sFrameID = StringReplace($sFrameID,"TLEN", "TLE"); Length $sFrameID = StringReplace($sFrameID,"TMED", "TMT"); Media type $sFrameID = StringReplace($sFrameID,"TOAL", "TOT"); Original album/movie/show title $sFrameID = StringReplace($sFrameID,"TOFN", "TOF"); Original filename $sFrameID = StringReplace($sFrameID,"TOLY", "TOL"); Original lyricist(s)/text writer(s) $sFrameID = StringReplace($sFrameID,"TOPE", "TOA"); Original artist(s)/performer(s) $sFrameID = StringReplace($sFrameID,"TORY", "TOR"); Original release year $sFrameID = StringReplace($sFrameID,"TPE1", "TP1"); Lead performer(s)/Soloist(s) $sFrameID = StringReplace($sFrameID,"TPE2", "TP2"); Band/orchestra/accompaniment $sFrameID = StringReplace($sFrameID,"TPE3", "TP3"); Conductor/performer refinement $sFrameID = StringReplace($sFrameID,"TPE4", "TP4"); Interpreted, remixed, or otherwise modified by $sFrameID = StringReplace($sFrameID,"TPOS", "TPA"); Part of a set $sFrameID = StringReplace($sFrameID,"TPUB", "TPB"); Publisher $sFrameID = StringReplace($sFrameID,"TRCK", "TRK"); Track number/Position in set $sFrameID = StringReplace($sFrameID,"TRDA", "TRD"); Recording dates $sFrameID = StringReplace($sFrameID,"TSIZ", "TSI"); Size $sFrameID = StringReplace($sFrameID,"TSRC", "TRC"); ISRC - International Standard Recording Code $sFrameID = StringReplace($sFrameID,"TSSE", "TSS"); Software/Hardware and settings used for encoding $sFrameID = StringReplace($sFrameID,"TYER", "TYE"); Year $sFrameID = StringReplace($sFrameID,"TXXX", "TXX"); User defined text information frame $sFrameID = StringReplace($sFrameID,"UFID", "UFI"); Unique file identifier $sFrameID = StringReplace($sFrameID,"USLT", "ULT"); Unsychronized lyric/text transcription $sFrameID = StringReplace($sFrameID,"WCOM", "WCM"); Commercial information $sFrameID = StringReplace($sFrameID,"WCOP", "WCP"); Copyright/Legal information $sFrameID = StringReplace($sFrameID,"WOAF", "WAF"); Official audio file webpage $sFrameID = StringReplace($sFrameID,"WOAR", "WAR"); Official artist/performer webpage $sFrameID = StringReplace($sFrameID,"WOAS", "WAS"); Official audio source webpage $sFrameID = StringReplace($sFrameID,"WPUB", "WPB"); Publishers official webpage $sFrameID = StringReplace($sFrameID,"WXXX", "WXX"); User defined URL link frame EndFunc Func _MPEG_GetFrameHeader($sFilename) #cs #ID3.au3 UDF Latest Changes.......: ;================================ http://www.mp3-tech.org/programmer/frame_header.html Frame Definition AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM A - Frame Sync 0xFFE0 B - MPEG Audio version ID 00 - MPEG Version 2.5 (later extension of MPEG 2) 01 - reserved 10 - MPEG Version 2 (ISO/IEC 13818-3) 11 - MPEG Version 1 (ISO/IEC 11172-3) C - Layer description 00 - reserved 01 - Layer III 10 - Layer II 11 - Layer I D - Protection bit 0 - Protected by CRC (16bit CRC follows header) 1 - Not protected E - Bitrate index F - Sampling rate frequency index G - Padding bit H - Private bit. This one is only informative. I - Channel Mode J - Mode extension (Only used in Joint stereo) K - Copyright L - Original M - Emphasis #ce ;==================================================================== ;Check MPEG Header Local $ID3v2TagSize = 0 Local $hfile = FileOpen($sFilename,16) ;open in binary mode Local $bID3v2_TagID = FileRead($hfile,3) If BinaryToString($bID3v2_TagID) == "ID3" Then Local $bId3TagHeader = $bID3v2_TagID & FileRead($hfile,7) $ID3v2TagSize = _ID3v2Tag_GetTagSize($bId3TagHeader) ;~ MsgBox(0,"$ID3v2TagSize",$ID3v2TagSize) EndIf FileSetPos($hfile, $ID3v2TagSize, $FILE_BEGIN) ;Sync with first 2 bytes of MPEG Frame Header Local $SyncBytes = FileRead($hfile,2) Local $ReadError = @error Local $byteNum = 0 While BitAND($SyncBytes, Binary("0xFFE0")) <> Binary("0xFFE0") $byteNum += 2 $SyncBytes = FileRead($hfile,2) $ReadError = @error If $ReadError == -1 Then FileClose($hfile) Return -1 EndIf WEnd ;~ MsgBox(0,"$byteNum",$byteNum) $SyncBytes &= FileRead($hfile,2) FileClose($hfile) If _h_MPEG_IsValidHeader($SyncBytes) Then Return $SyncBytes Else Return -1 EndIf EndFunc Func _h_MPEG_IsValidHeader($MPEGFrameSyncHex) ;~ MsgBox(0,"$MPEGFrameSyncHex",$MPEGFrameSyncHex) $MPEGFrameSyncUint32 = Dec(StringReplace($MPEGFrameSyncHex,"0x",""),2) If $MPEGFrameSyncUint32 > Dec("FFE00000",2) Then If $MPEGFrameSyncUint32 < Dec("FFFFEC00",2) Then If Not(StringMid($MPEGFrameSyncHex,4,1) == "0") Then If Not(StringMid($MPEGFrameSyncHex,4,1) == "1") Then If Not(StringMid($MPEGFrameSyncHex,4,1) == "9") Then ;valid MPEG Header Found Return 1 EndIf EndIf EndIf EndIf EndIf Return 0 EndFunc Func _MPEG_GetVersion($bMPEGFrameHeader) ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;B - MPEG Audio version ID ; 00 - MPEG Version 2.5 (later extension of MPEG 2) ; 01 - reserved ; 10 - MPEG Version 2 (ISO/IEC 13818-3) ; 11 - MPEG Version 1 (ISO/IEC 11172-3) Local $bMPEGVersion = BitShift(BitAND(24,BinaryMid($bMPEGFrameHeader,2,1)),3) Local $sMPEGVersion = "" Switch $bMPEGVersion Case 0 $sMPEGVersion = "MPEG Version 2.5" Case 1 $sMPEGVersion = "Reserved" Case 2 $sMPEGVersion = "MPEG Version 2" Case 3 $sMPEGVersion = "MPEG Version 1" EndSwitch ;~ MsgBox(0,$bMPEGVersion,$sMPEGVersion) Return $sMPEGVersion EndFunc Func _MPEG_GetLayer($bMPEGFrameHeader) ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;C - Layer description ; 00 - reserved ; 01 - Layer III ; 10 - Layer II ; 11 - Layer I Local $bMPEGLayer = BitShift(BitAND(6,BinaryMid($bMPEGFrameHeader,2,1)),1) Local $sMPEGLayer = "" Switch $bMPEGLayer Case 0 $sMPEGLayer = "Reserved" Case 1 $sMPEGLayer = "Layer III" Case 2 $sMPEGLayer = "Layer II" Case 3 $sMPEGLayer = "Layer I" EndSwitch ;~ MsgBox(0,$bMPEGLayer,$sMPEGLayer) Return $sMPEGLayer EndFunc Func _MPEG_GetBitRate($bMPEGFrameHeader) ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;E - Bitrate index ;bits V1,L1 V1,L2 V1,L3 V2,L1 V2, L2 & L3 ;0000 free free free free free ;0001 32 32 32 32 8 ;0010 64 48 40 48 16 ;0011 96 56 48 56 24 ;0100 128 64 56 64 32 ;0101 160 80 64 80 40 ;0110 192 96 80 96 48 ;0111 224 112 96 112 56 ;1000 256 128 112 128 64 ;1001 288 160 128 144 80 ;1010 320 192 160 160 96 ;1011 352 224 192 176 112 ;1100 384 256 224 192 128 ;1101 416 320 256 224 144 ;1110 448 384 320 256 160 ;1111 bad bad bad bad bad ;NOTES: All values are in kbps ; V1 - MPEG Version 1 ; V2 - MPEG Version 2 and Version 2.5 ; L1 - Layer I ; L2 - Layer II ; L3 - Layer III ; "free" means free format. The free bitrate must remain constant, ; an must be lower than the maximum allowed bitrate. Decoders are not ; required to support decoding of free bitrate streams. "bad" means that the value is unallowed. Local $bMPEGVersion = BitShift(BitAND(24,BinaryMid($bMPEGFrameHeader,2,1)),3) Local $bMPEGLayer = BitShift(BitAND(6,BinaryMid($bMPEGFrameHeader,2,1)),1) Local $bMPEGBitrateIndex = BitShift(BitAND(240,BinaryMid($bMPEGFrameHeader,3,1)),4) Local $sMPEGBitrate = "" Switch $bMPEGBitrateIndex Case 0 ;0000 free free free free free $sMPEGBitrate = "free" Case 1 ;0001 32 32 32 32 8 $sMPEGBitrate = "32" If ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "8" EndIf Case 2 ;0010 64 48 40 48 16 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "64" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "48" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "40" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "48" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "16" EndIf Case 3 ;0011 96 56 48 56 24 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "96" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "56" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "48" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "56" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "24" EndIf Case 4 ;0100 128 64 56 64 32 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "128" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "64" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "56" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "64" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "32" EndIf Case 5 ;0101 160 80 64 80 40 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "160" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "80" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "64" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "80" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "40" EndIf Case 6 ;0110 192 96 80 96 48 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "192" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "96" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "80" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "96" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "48" EndIf Case 7 ;0111 224 112 96 112 56 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "224" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "112" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "96" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "112" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "56" EndIf Case 8 ;1000 256 128 112 128 64 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "256" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "128" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "112" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "128" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "64" EndIf Case 9 ;1001 288 160 128 144 80 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "288" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "160" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "128" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "144" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "80" EndIf Case 10 ;1010 320 192 160 160 96 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "320" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "192" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "160" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "160" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "96" EndIf Case 11 ;1011 352 224 192 176 112 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "352" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "224" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "192" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "176" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "112" EndIf Case 12 ;1100 384 256 224 192 128 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "384" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "256" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "224" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "192" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "128" EndIf Case 13 ;1101 416 320 256 224 144 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "416" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "320" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "256" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "224" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "144" EndIf Case 14 ;1110 448 384 320 256 160 If ($bMPEGVersion == 3) and ($bMPEGLayer == 3) Then ;V1,L1 $sMPEGBitrate = "448" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 2) Then ;V1,L2 $sMPEGBitrate = "384" Elseif ($bMPEGVersion == 3) and ($bMPEGLayer == 1) Then ;V1,L3 $sMPEGBitrate = "320" Elseif ($bMPEGVersion == 2) and ($bMPEGLayer == 3) Then ;V2,L1 $sMPEGBitrate = "256" Elseif ($bMPEGVersion == 2) and (($bMPEGLayer == 2) or ($bMPEGLayer == 1)) Then ;V2,L2 or V2,L3 $sMPEGBitrate = "160" EndIf Case 15 ;1111 bad bad bad bad bad $sMPEGBitrate = "bad" EndSwitch Return $sMPEGBitrate EndFunc Func _MPEG_GetSampleRate($bMPEGFrameHeader) ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;F - Sampling rate frequency index ;bits MPEG1 MPEG2 MPEG2.5 ;00 44100 Hz 22050 Hz 11025 Hz ;01 48000 Hz 24000 Hz 12000 Hz ;10 32000 Hz 16000 Hz 8000 Hz ;11 reserv. reserv. reserv. Local $bMPEGVersion = BitShift(BitAND(24,BinaryMid($bMPEGFrameHeader,2,1)),3) Local $bMPEGSampleRateIndex = BitShift(BitAND(12,BinaryMid($bMPEGFrameHeader,3,1)),2) Local $sMPEGSampleRate = "" Switch $bMPEGSampleRateIndex Case 0 Switch $bMPEGVersion Case 0 ;MPEG Version 2.5 $sMPEGSampleRate = "11025 Hz" Case 2 ;MPEG Version 2 $sMPEGSampleRate = "22050 Hz" Case 3 ;MPEG Version 1 $sMPEGSampleRate = "44100 Hz" EndSwitch Case 1 Switch $bMPEGVersion Case 0 ;MPEG Version 2.5 $sMPEGSampleRate = "12000 Hz" Case 2 ;MPEG Version 2 $sMPEGSampleRate = "24000 Hz" Case 3 ;MPEG Version 1 $sMPEGSampleRate = "48000 Hz" EndSwitch Case 2 Switch $bMPEGVersion Case 0 ;MPEG Version 2.5 $sMPEGSampleRate = "8000 Hz" Case 2 ;MPEG Version 2 $sMPEGSampleRate = "16000 Hz" Case 3 ;MPEG Version 1 $sMPEGSampleRate = "32000 Hz" EndSwitch Case 3 $sMPEGSampleRate = "Reserved" EndSwitch Return $sMPEGSampleRate EndFunc Func _MPEG_GetChannelMode($bMPEGFrameHeader) ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;I - Channel Mode ; 00 - Stereo ; 01 - Joint stereo (Stereo) ; 10 - Dual channel (2 mono channels) ; 11 - Single channel (Mono) Local $bMPEGChannelMode = BitShift(BitAND(192,BinaryMid($bMPEGFrameHeader,4,1)),6) Local $sMPEGChannelMode = "" Switch $bMPEGChannelMode Case 0 $sMPEGChannelMode = "Stereo" Case 1 $sMPEGChannelMode = "Joint Stereo" Case 2 $sMPEGChannelMode = "Dual Channel" Case 3 $sMPEGChannelMode = "Single channel (Mono)" EndSwitch Return $sMPEGChannelMode EndFunc Func _MPEG_GetChannelModeEx($bMPEGFrameHeader) ;TODO - Finish ;AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM ;J - Mode extension (Only used in Joint stereo) ;Mode extension is used to join informations that are of no use for stereo effect, ;thus reducing needed bits. These bits are dynamically determined by an encoder in ;Joint stereo mode, and Joint Stereo can be changed from one frame to another, or ;even switched on or off. ;Complete frequency range of MPEG file is divided in subbands There are 32 subbands. ;For Layer I & II these two bits determine frequency range (bands) where intensity ;stereo is applied. For Layer III these two bits determine which type of joint stereo ;is used (intensity stereo or m/s stereo). Frequency range is determined within decompression algorithm. ; Layer I and II Layer III ;value Layer I & II Intensity stereo MS stereo ;00 bands 4 to 31 off off ;01 bands 8 to 31 on off ;10 bands 12 to 31 off on ;11 bands 16 to 31 on on Local $bMPEGLayer = BitShift(BitAND(6,BinaryMid($bMPEGFrameHeader,2,1)),1) Local $bMPEGChannelModeEx = BitShift(BitAND(48,BinaryMid($bMPEGFrameHeader,4,1)),4) Local $sMPEGChannelModeEx = "" Switch $bMPEGChannelModeEx Case 0 $sMPEGChannelModeEx = "" Case 1 $sMPEGChannelModeEx = "" Case 2 $sMPEGChannelModeEx = "" Case 3 $sMPEGChannelModeEx = "" EndSwitch Return $sMPEGChannelModeEx EndFunc Func _APEv2Tag_ReadFromFile($sFilename) ;~ This is how information is laid out in an APEv2 tag: ;~ APE Tags Header 32 bytes ;~ APE Tag Item 1 10.. bytes ;~ APE Tag Item 2 10.. bytes ;~ ... 10.. bytes ;~ APE Tag Item n-1 10.. bytes ;~ APE Tag Item n 10.. bytes ;~ APE Tags Footer 32 bytes ;~ APE tag items should be sorted ascending by size. When streaming, parts of the ;~ APE tags can be dropped to reduce danger of drop outs between titles. This is ;~ not a must, but strongly recommended. Actually the items should be sorted by ;~ importance/byte, but this is not feasible. Only break this rule if you add less ;~ important small items and you don't want to rewrite the whole tag. An APE tag at ;~ the end of a file (strongly recommended) must have at least a footer, an APE tag ;~ in the beginning of a file (strongly unrecommended) must have at least a header. ;~ When located at the end of an MP3 file, an APE tag should be placed after the the ;~ last frame, just before the ID3v1 tag (if any). Local $APEv2_TAGINFO = "", $APE_TagFound = False,$hfile, $iID3v1_ByteOffset = 0 Local $APE_Version,$APE_TagSize, $sAPE_ItemKeys = "" If _ID3v1Tag_ReadFromFile($sFilename) <> "" Then $iID3v1_ByteOffset = 128 EndIf $hfile = FileOpen($sFilename,16) ;open in binary mode FileSetPos($hFile, -($iID3v1_ByteOffset+32), $FILE_END) ;include 32 bytes to check for APETAG Footer $APE_Footer = FileRead($hfile,32) $APETAG_ID = BinaryToString(BinaryMid($APE_Footer,1,8)) $APEv2_TagFrameString = $sFilename ;APE TAG Test Code ;~ MsgBox(0,"$APETAG_ID",$APETAG_ID) If StringCompare($APETAG_ID,"APETAGEX") == 0 Then $APE_TagFound = True $APE_Version = Hex(BinaryMid($APE_Footer,12,1),2) $APE_Version &= Hex(BinaryMid($APE_Footer,11,1),2) $APE_Version &= Hex(BinaryMid($APE_Footer,10,1),2) $APE_Version &= Hex(BinaryMid($APE_Footer,9,1),2) $APE_Version = Dec($APE_Version) $APE_TagSize = Hex(BinaryMid($APE_Footer,16,1),2) $APE_TagSize &= Hex(BinaryMid($APE_Footer,15,1),2) $APE_TagSize &= Hex(BinaryMid($APE_Footer,14,1),2) $APE_TagSize &= Hex(BinaryMid($APE_Footer,13,1),2) $APE_TagSize = Dec($APE_TagSize,2) ;APE_TagSize does not include 32 byte header but does include 32 byte footer ;~ MsgBox(0,"$APETAG",$APETAG_ID & @CRLF & "TagVersion = " & $APE_Version & @CRLF & "TagSize = " & $APE_TagSize) FileSetPos($hFile, -($iID3v1_ByteOffset+$APE_TagSize + 32), $FILE_END) $APEv2_RawDataBinary = FileRead($hfile,$APE_TagSize) ;~ MsgBox(0,"$APEv2_RawDataBinary",$APEv2_RawDataBinary) FileClose($hfile) ;iTunes TagSize includes header (Not to standard) Dim $iTunesTest = StringInStr(BinaryToString($APEv2_RawDataBinary),"APETAGEX") $APEv2_RawDataBinary = BinaryMid($APEv2_RawDataBinary,$iTunesTest) $sAPE_ItemKeys = _h_APEv2_EnumerateItemKeys() Else $APEv2_RawDataBinary = 0 EndIf If $APE_TagFound Then $APEv2_TAGINFO &= "APEv" & String($APE_Version/1000) & $sAPE_ItemKeys & @CRLF EndIf If $APE_TagFound Then SetExtended(4) EndIf Return $APEv2_TAGINFO EndFunc Func _h_APEv2_EnumerateItemKeys() Local $APE_TAGINFO = "" If BinaryLen($APEv2_RawDataBinary) < 32 Then Return $APE_TAGINFO EndIf Local $APE_TagSize = _APEv2Tag_GetTagSize() Local $iStartByte = 32,$APE_ItemSize,$APE_ItemKeyterm,$APE_ItemKey While ($iStartByte+1) < ($APE_TagSize-32) $APE_ItemSize = Hex(BinaryMid($APEv2_RawDataBinary,$iStartByte + 4,1),2) $APE_ItemSize &= Hex(BinaryMid($APEv2_RawDataBinary,$iStartByte + 3,1),2) $APE_ItemSize &= Hex(BinaryMid($APEv2_RawDataBinary,$iStartByte + 2,1),2) $APE_ItemSize &= Hex(BinaryMid($APEv2_RawDataBinary,$iStartByte + 1,1),2) $APE_ItemSize = Dec($APE_ItemSize) ;~ MsgBox(0,"$APE_ItemSize",$APE_ItemSize) $APE_ItemKeyterm = StringInStr(BinaryToString(BinaryMid($APEv2_RawDataBinary,$iStartByte + 9)),chr(0)) $APE_ItemKey = BinaryToString(BinaryMid($APEv2_RawDataBinary,$iStartByte + 9,$APE_ItemKeyterm-1)) ;~ MsgBox(0,"$APE_ItemKey",$APE_ItemKey) $APE_TAGINFO &= "|" & $APE_ItemKey $APEv2_TagFrameString &= @CRLF & $APE_ItemKey & "|" & String($iStartByte + $APE_ItemKeyterm + 9) & "|" & String($APE_ItemSize) $iStartByte = $iStartByte + 8 + $APE_ItemKeyterm + $APE_ItemSize ;~ MsgBox(0,"$iStartByte",$iStartByte & " of " & ($APE_TagSize-32)) WEnd ;~ MsgBox(0,"$APEv2_TagFrameString",$APEv2_TagFrameString) Return $APE_TAGINFO EndFunc Func _APEv2Tag_GetVersion() If BinaryLen($APEv2_RawDataBinary) < 32 Then Return 0 EndIf Local $APE_Version = BinaryMid($APEv2_RawDataBinary,9,4) $APE_Version = Number($APE_Version) Return $APE_Version EndFunc Func _APEv2Tag_GetTagSize() If BinaryLen($APEv2_RawDataBinary) < 32 Then Return 0 EndIf Local $APE_TagSize = BinaryMid($APEv2_RawDataBinary,13,4) $APE_TagSize = Number($APE_TagSize) Return $APE_TagSize EndFunc Func _APEv2Tag_GetItemCount() If BinaryLen($APEv2_RawDataBinary) < 32 Then Return 0 EndIf Local $APE_ItemCount = BinaryMid($APEv2_RawDataBinary,17,4) $APE_ItemCount = Number($APE_ItemCount) Return $APE_ItemCount EndFunc Func _APEv2_GetItemKeys($StringLineDelimiter = @CRLF) ;If $StringLineDelimiter == -1 then return an array Local $vAPE_ItemKeys If BinaryLen($APEv2_RawDataBinary) < 32 Then Return "" EndIf Local $sItemKeyList = StringSplit($APEv2_TagFrameString,@CRLF,1) If $StringLineDelimiter == -1 Then Dim $vAPE_ItemKeys[1] $vAPE_ItemKeys[0] = $sItemKeyList[0]-1 EndIf Local $aItemKey For $iKey = 2 To $sItemKeyList[0]-1 $aItemKey = StringSplit($sItemKeyList[$iKey],"|") If $StringLineDelimiter == -1 Then _ArrayAdd($vAPE_ItemKeys,$aItemKey[1]) Else $vAPE_ItemKeys &= $aItemKey[1] & $StringLineDelimiter EndIf Next $aItemKey = StringSplit($sItemKeyList[$sItemKeyList[0]],"|") If $StringLineDelimiter == -1 Then _ArrayAdd($vAPE_ItemKeys,$aItemKey[1]) ;~ _ArrayDisplay($vAPE_ItemKeys) Else $vAPE_ItemKeys &= $aItemKey[1] ;~ MsgBox(0,"$vAPE_ItemKeys",$vAPE_ItemKeys) EndIf Return $vAPE_ItemKeys EndFunc Func _APEv2_GetItemValueBinary($sAPE_ItemKey) ;First Get Start Byte and Frame Length from $ID3v2_TagFrameString Local $iFrameInfo = StringInStr($APEv2_TagFrameString,$sAPE_ItemKey,-1) Local $FrameInfoCut = StringMid($APEv2_TagFrameString,$iFrameInfo) Local $iFrameInfoEnd = StringInStr($FrameInfoCut,@CRLF) $FrameInfoCut = StringMid($FrameInfoCut,1,$iFrameInfoEnd-1) $Firstpipe = StringInStr($FrameInfoCut,'|') $Lastpipe = StringInStr($FrameInfoCut,'|',-1,-1) Local $FrameStart = Number(StringMid($FrameInfoCut,$Firstpipe+1,($Lastpipe)-($Firstpipe+1))) Local $FrameSize = Number(StringMid($FrameInfoCut,$Lastpipe+1)) ;MsgBox(0,"$FrameStart",$FrameStart) ;MsgBox(0,"$FrameSize",$FrameSize) Local $bItemValueData = BinaryMid($APEv2_RawDataBinary,$FrameStart,$FrameSize) ;~ MsgBox(0,$sAPE_ItemKey,$bItemValueData) If BinaryLen($bItemValueData) <> 0 Then Return $bItemValueData Else Return -1 EndIf EndFunc Func _APEv2_GetItemValueString($sAPE_ItemKey = -1,$StringLineDelimiter = @CRLF) ;TODO Test when $sAPE_ItemKey = -1 If BinaryLen($APEv2_RawDataBinary) < 9 Then Return "" EndIf Local $iNumValues = 1 Local $sAPEv2_ReturnString = "" If $sAPE_ItemKey == -1 Then Local $ataginfo = StringSplit($APEv2_TagFrameString, @CRLF, 1) For $istr = 2 to $ataginfo[0] Dim $a2taginfo = StringSplit($ataginfo[$istr], "|") If $a2taginfo[0] > 1 Then Local $bValue = _APEv2_GetItemValueBinary($a2taginfo[1]) Local $sImageCheck = StringStripWS(BinaryToString($bValue),3) ;~ MsgBox(0,$sImageCheck,StringCompare($sImageCheck, ($a2taginfo[1] & ".jpg"))) If StringInStr($sImageCheck, ".jpg") > 0 Then ;~ MsgBox(0,$sImageCheck,($a2taginfo[1] & ".jpg")) Local $iImageIndex = StringInStr(BinaryToString($bValue),chr(0)) Local $bImage = BinaryMid($bValue,$iImageIndex+1) FileWrite($sImageCheck,$bImage) $sAPEv2_ReturnString &= $a2taginfo[1] & " = " & $sImageCheck & $StringLineDelimiter Else ;Check if string has multiple values delimited by 0x00 (Mp3tag does this with multiple COMMENT Itemkeys) Local $aValues = StringSplit(BinaryToString($bValue),chr(0)) $iNumValues = $aValues[0] ;~ MsgBox(0,"$iNumValues",$iNumValues) ;~ _ArrayDisplay($aValues) If $aValues[0] > 0 Then For $i = 1 To $aValues[0] $sAPEv2_ReturnString &= $a2taginfo[1] & " = " & $aValues[$i] & $StringLineDelimiter Next Else $sAPEv2_ReturnString &= $a2taginfo[1] & " = " & BinaryToString($bValue) & $StringLineDelimiter EndIf EndIf EndIf Next Else Local $bValue = _APEv2_GetItemValueBinary($sAPE_ItemKey) Local $aValues = StringSplit(BinaryToString($bValue),chr(0)) $iNumValues = $aValues[0] ;~ _ArrayDisplay($aValues) If $aValues[0] > 0 Then If IsNumber($StringLineDelimiter) Then $sAPEv2_ReturnString = $aValues[$StringLineDelimiter] Else $sAPEv2_ReturnString = $aValues[1] ;~ For $i = 1 To $aValues[0] ;~ $sAPEv2_ReturnString &= $aValues[$i] & $StringLineDelimiter ;~ Next EndIf Else $sAPEv2_ReturnString = BinaryToString($bValue) EndIf If StringInStr($sAPEv2_ReturnString, ".jpg") > 0 Then ;~ MsgBox(0,$sAPE_ItemKey,$sAPEv2_ReturnString) Local $iImageIndex = StringInStr(BinaryToString($bValue),chr(0)) Local $bImage = BinaryMid($bValue,$iImageIndex+1) FileWrite($sAPEv2_ReturnString,$bImage) $iNumValues = 1 EndIf EndIf ;~ MsgBox(0,"$sAPE_ItemKey",$sAPEv2_ReturnString) SetExtended($iNumValues) Return $sAPEv2_ReturnString EndFunc Func _APEv2_RemoveTag($Filename) Local $sAPEv2_TagInfo = _APEv2Tag_ReadFromFile($Filename) If StringLen($sAPEv2_TagInfo) <= 1 Then Return -1 EndIf Local $APE_TagSize = _APEv2Tag_GetTagSize() ;need to check if ID3v1 Tag is present Local $StartOfTag = FileGetSize($Filename) - (128+$APE_TagSize+32) Local $EndOfTag = $StartOfTag + $APE_TagSize + 32 Local $TagFilename = StringTrimRight($Filename,4) & "_APETAG.mp3" ;Open Tag File write mode, create file, binary mode $hTagFile = Fileopen($TagFilename,2+8+16) ;erase all $hFile = Fileopen($Filename,16) ;binary mode FileSetPos($hFile, 0, $FILE_BEGIN) FileWrite($hTagFile,FileRead($hFile,$StartOfTag)) ;read and write all data before APEv2 Tag FileSetPos($hFile, $EndOfTag, $FILE_BEGIN) ;Skip APEv2 Tag Data FileWrite($hTagFile,FileRead($hFile)) ;Write in rest of file FileClose($hTagFile) FileClose($hFile) FileCopy($TagFilename,$Filename,1) FileDelete($TagFilename) $APEv2_RawDataBinary = 0 EndFunc