Share your advanced PureBasic knowledge/code with the community.
ebs
Enthusiast
Posts: 557 Joined: Fri Apr 25, 2003 11:08 pm
Post
by ebs » Fri Jul 18, 2003 4:25 am
The subject says it all! Here's the code:
Code: Select all
Structure ID3v2
Title.s
Artist.s
Album.s
Comment.s
Year.s
Genre.s
Composer.s
URL.s
OrgArtist.s
Copyright.s
EncodedBy.s
Encoder.s
Track.s
EndStructure
Global TagID3v2.ID3v2
Declare.l GetID3v2Tag(FileName.s)
Repeat
MP3File.s = OpenFileRequester("Select MP3 File", "", "MP3 Files (*.mp3)|*.mp3", 0)
If GetID3v2Tag(MP3File)
; display tag contents
Debug "Album: " + TagID3v2\Album
Debug "Artist: " + TagID3v2\Artist
Debug "Comment: " + TagID3v2\Comment
Debug "Composer: " + TagID3v2\Composer
Debug "Copyright: " + TagID3v2\Copyright
Debug "Encoded by: " + TagID3v2\EncodedBy
Debug "Encoder: " + TagID3v2\Encoder
Debug "Genre: " + TagID3v2\Genre
Debug "Original Artist: " + TagID3v2\OrgArtist
Debug "Title: " + TagID3v2\Title
Debug "Track: " + TagID3v2\Track
Debug "URL: " + TagID3v2\URL
Debug "Year: " + TagID3v2\Year
Debug ""
EndIf
Until MP3File = ""
End
;- read ID3v2.3.x tag fields from MP3 file
Procedure.l GetID3v2Tag(FileName.s)
If FileName = ""
ProcedureReturn #False
EndIf
ReadFile(1, FileName)
ID3.s = Space(3)
ReadData(@ID3, 3)
; must be ID3v2.3.x
If ID3 = "ID3" And ReadByte() = $03
ID3Exists = #True
; skip revision and flag bytes
ReadWord()
; get tag size
ID3Size.l = 0
For Byte.l = 3 To 0 Step -1
ID3Size + (ReadByte() << (7*Byte))
Next
Else
; no ID3v2.3.x tag present
CloseFile(1)
ProcedureReturn #False
EndIf
; clear tag contents
TagID3v2\Album = ""
TagID3v2\Artist = ""
TagID3v2\Comment = ""
TagID3v2\Composer = ""
TagID3v2\Copyright = ""
TagID3v2\EncodedBy = ""
TagID3v2\Encoder = ""
TagID3v2\Genre = ""
TagID3v2\OrgArtist = ""
TagID3v2\Title = ""
TagID3v2\Track = ""
TagID3v2\URL = ""
TagID3v2\Year = ""
Size.l = 0
Repeat
; get frames until no more
FrameID.s = Space(4)
ReadData(@FrameID, 4)
If Asc(Left(FrameID, 1)) = 0
; no more frames
CloseFile(1)
ProcedureReturn #True
EndIf
; get frame size
FrameSize.l = 0
For Byte.l = 3 To 0 Step -1
FrameSize + (ReadByte() << (7*Byte))
Next
; add frame size to total size
Size + FrameSize
; skip flag and language bytes
ReadWord()
ReadByte()
; get frame contents
; subtract one for language byte
FrameSize - 1
Contents.s = Space(FrameSize)
ReadData(@Contents, FrameSize)
; put frame contents into structure
Select FrameID
Case "TALB"
TagID3v2\Album = Contents
Case "TCOM"
TagID3v2\Composer = Contents
Case "TCON"
TagID3v2\Genre = Contents
Case "TCOP"
TagID3v2\Copyright = Contents
Case "TENC"
TagID3v2\EncodedBy = Contents
Case "TIT2"
TagID3v2\Title = Contents
Case "TOPE"
TagID3v2\OrgArtist = Contents
Case "TPE1"
TagID3v2\Artist = Contents
Case "TRCK"
TagID3v2\Track = Contents
Case "TSSE"
TagID3v2\Encoder = Contents
Case "TYER"
TagID3v2\Year = Contents
Case "COMM"
TagID3v2\Comment = Contents
Case "WXXX"
TagID3v2\URL = Contents
EndSelect
; stop if tag size reached/exceeded
Until Size >= ID3Size
CloseFile(1)
ProcedureReturn #True
EndProcedure
naw
Enthusiast
Posts: 573 Joined: Fri Apr 25, 2003 4:57 pm
Post
by naw » Fri Jul 18, 2003 3:34 pm
Nice Stuff - I'm in the process of converting something I found on the German forum - but the code is quite complex - I find this easier to understand...
I wonder, though - I use MusicMatch JukeBox which seems to use some non-standard ID3 Tags that it appears to dump into the "Comments" TAG...
I'm struggling to extract those TAGS & their values...
Ta - N
CONVERT
Enthusiast
Posts: 130 Joined: Fri May 02, 2003 12:19 pm
Location: France
Post
by CONVERT » Sat Jul 30, 2005 3:18 pm
Thank you very much, ebs, for your code. It allows me to make jackets for my CD.
Jean.
PureBasic 6.20 beta 2 (x64) | Windows 10 Pro x64 | Intel(R) Core(TM) i7-8700 CPU @ 3.20Ghz 16 GB RAM, SSD 500 GB, PC locally assembled.
Come back to 6.11 LTS 64 bits because of an issue with #PB_ComboBox_UpperCase in ComboBoxGadget() (Oct. 10, 2024).
Killswitch
Enthusiast
Posts: 731 Joined: Wed Apr 21, 2004 7:12 pm
Post
by Killswitch » Sat Jul 30, 2005 5:35 pm
Is this code freeware? i.e. Is it ok to be used in commercial applications?
~I see one problem with your reasoning: the fact is thats not a chicken~
ebs
Enthusiast
Posts: 557 Joined: Fri Apr 25, 2003 11:08 pm
Post
by ebs » Sat Jul 30, 2005 7:14 pm
Killswitch wrote: Is this code freeware? i.e. Is it ok to be used in commercial applications?
Anything I post here is meant to be used by others, in any application you like (freeware, commercial or otherwise).
Go right ahead, but please include me in your "about" box or readme file!
Regards,
Eric Schuyler
orange-blue
User
Posts: 24 Joined: Wed Apr 06, 2005 10:21 am
Post
by orange-blue » Fri Aug 05, 2005 8:05 pm
nice, but I don't have many songs with ID3 v2.x Tags
Do you also have a code for reading ID3 v1 Tags?
Would be nice.
KarLKoX
Enthusiast
Posts: 681 Joined: Mon Oct 06, 2003 7:13 pm
Location: France
Contact:
Post
by KarLKoX » Fri Aug 05, 2005 9:31 pm
Code: Select all
Structure ID3TagV1Tag
tag.s
title.s
artist.s
album.s
year.s
comments.s
genre.s
track.s
EndStructure
#READ_INPUT_FILE_TAG = 1
#WRITE_INPUT_FILE_TAG = 2
Procedure.s padString(theString.s, dummy.b)
Protected m_idx.b
Protected m_lent.l
Protected m_newString.s
If dummy
m_len = 28
Else
m_len = 30
EndIf
m_newString = theString
For m_idx = Len(theString) To m_len
m_newString + Chr(0)
Next m_idx
Debug "m_newString.s : " + m_newString.s
ProcedureReturn m_newString
EndProcedure
;---- HasTagV1
; Purpose: Check if a Tag V1.x exists
; Require: file ==> input file
; Return : #TRUE if the tag exists
; #FALSE otherwise
Procedure HasTagV1(file.s)
Protected hOrgFile.l
Protected m_tag.s
hOrgFile = ReadFile(#pb_any, file)
If (hOrgFile <= 0)
MessageRequester("HasTagV1::Error", "Error occured while reading " + file, 0)
ProcedureReturn #FALSE
EndIf
FileSeek(Lof()-128)
m_tag = Space(3)
ReadData(@m_tag, 3)
; Rewind
FileSeek(0)
CloseFile(hOrgFile)
If (m_tag = "TAG")
ProcedureReturn #TRUE
Else
ProcedureReturn #FALSE
EndIf
EndProcedure
;---- ReadTagV1
; Purpose: Read a IDTag V1.0/1.1
; Require: file ==> input file
; Return : fill the m_tagV1Info structure if the tag exist
Procedure.b ReadTagV1(file.s)
Protected hOrgFile.l
Dim m_temp.b(128)
If HasTagV1(file) = #FALSE
;MessageRequester("Error", "No IdTagV1 found.", 0)
ProcedureReturn #FALSE
EndIf
hOrgFile = ReadFile(#READ_INPUT_FILE_TAG, file)
If (hOrgFile <= 0)
MessageRequester("ReadTagV1::Error", "Error occured while reading " + file, 0)
ProcedureReturn #FALSE
EndIf
FileSeek(Lof()-128)
ReadData(@m_temp(), 128)
CloseFile(#READ_INPUT_FILE_TAG)
m_tagV1Info\tag = PeekS(m_temp(),3)
m_tagV1Info\title = PeekS(m_temp()+3,30)
m_tagV1Info\artist = PeekS(m_temp()+33,30)
m_tagV1Info\album = PeekS(m_temp()+63,30)
m_tagV1Info\year = PeekS(m_temp()+93,4)
m_tagV1Info\comments = PeekS(m_temp()+97,30)
; IdTag V1.1 extension : the last comments byte is the track number
If Right(m_tagV1Info\comments, 1) <> ""
m_tagV1Info\track = PeekS(m_temp()+126, 1) ;Right(m_tagV1Info\comments, 1)
EndIf
m_tagV1Info\genre = Chr(PeekB(m_temp()+127))
ProcedureReturn #TRUE
EndProcedure
;---- WriteTagV1
; Purpose: Write a IDtag V1.0/1.1
; Require: file ==> input file
; bRemove ==> #TRUE if you want only to remove the tag
; #FALSE otherwise
; Return : nothing
Procedure WriteTagV1(file.s, bRemove.b, *theTag.ID3TagV1Tag)
Protected hOrgFile.l
Protected m_compteur.l
Protected m_ID3.s
Protected m_offset.l
If HasTagV1(fichier)
m_offset = 128
Else
m_offset = 0
EndIf
hOrgFile = OpenFile(#WRITE_INPUT_FILE_TAG, file)
If (hOrgFile = 0)
MessageRequester("WriteTagV1::Error", "Error occured while reading " + file, 0)
ProcedureReturn
EndIf
UseFile(#WRITE_INPUT_FILE_TAG)
FileSeek(Lof()-m_offset)
If bRemove = #FALSE
m_ID3 = Space(3)
m_ID3 = "TAG"
WriteData(@m_ID3, 3)
WriteData(*theTag\title, 30)
WriteData(*theTag\artist, 30)
WriteData(*theTag\album, 30)
WriteData(*theTag\year, 4)
; Tag 1.1 extension : write the track
If (Str(*theTag\track) <> "")
dummy$ = padString(*theTag\comments, #TRUE)
WriteData(@dummy$, 28)
WriteByte(0)
WriteByte(Val(*theTag\track))
Else
WriteData(padString(*theTag\comments, #FALSE), 30)
EndIf
WriteByte(Val(*theTag\genre))
EndIf
CloseFile(#WRITE_INPUT_FILE_TAG)
EndProcedure
Last edited by
KarLKoX on Fri Aug 05, 2005 11:40 pm, edited 2 times in total.
Joakim Christiansen
Addict
Posts: 2452 Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:
Post
by Joakim Christiansen » Sat May 27, 2006 4:37 pm
I converted this to PB4:
Code: Select all
Structure ID3v2
Length.s
Title.s
Artist.s
Album.s
Comment.s
Year.s
Genre.s
Composer.s
URL.s
OrgArtist.s
Copyright.s
EncodedBy.s
Encoder.s
Track.s
EndStructure
Global TagID3v2.ID3v2
Declare.l GetID3v2Tag(FileName.s)
Repeat
MP3File.s = OpenFileRequester("Select MP3 File", "", "MP3 Files (*.mp3)|*.mp3", 0)
If GetID3v2Tag(MP3File)
; display tag contents
Debug "Album: " + TagID3v2\Album
Debug "Artist: " + TagID3v2\Artist
Debug "Comment: " + TagID3v2\Comment
Debug "Composer: " + TagID3v2\Composer
Debug "Copyright: " + TagID3v2\Copyright
Debug "Encoded by: " + TagID3v2\EncodedBy
Debug "Encoder: " + TagID3v2\Encoder
Debug "Genre: " + TagID3v2\Genre
Debug "Original Artist: " + TagID3v2\OrgArtist
Debug "Length: " + TagID3v2\Length
Debug "Title: " + TagID3v2\Title
Debug "Track: " + TagID3v2\Track
Debug "URL: " + TagID3v2\URL
Debug "Year: " + TagID3v2\Year
Debug ""
EndIf
Until MP3File = ""
End
;- read ID3v2.3.x tag fields from MP3 file
Procedure.l GetID3v2Tag(FileName.s)
If FileName = ""
ProcedureReturn #False
EndIf
ReadFile(1, FileName)
ID3.s = Space(3)
ReadData(1,@ID3, 3)
; must be ID3v2.3.x
If ID3 = "ID3" And ReadByte(1) = $03
ID3Exists = #True
; skip revision and flag bytes
ReadWord(1)
; get tag size
ID3Size.l = 0
For Byte.l = 3 To 0 Step -1
ID3Size + (ReadByte(1) << (7*Byte))
Next
Else
; no ID3v2.3.x tag present
CloseFile(1)
ProcedureReturn #False
EndIf
; clear tag contents
TagID3v2\Album = ""
TagID3v2\Artist = ""
TagID3v2\Comment = ""
TagID3v2\Composer = ""
TagID3v2\Copyright = ""
TagID3v2\EncodedBy = ""
TagID3v2\Encoder = ""
TagID3v2\Genre = ""
TagID3v2\OrgArtist = ""
TagID3v2\Length = ""
TagID3v2\Title = ""
TagID3v2\Track = ""
TagID3v2\URL = ""
TagID3v2\Year = ""
Size.l = 0
Repeat
; get frames until no more
FrameID.s = Space(4)
ReadData(1,@FrameID, 4)
If Asc(Left(FrameID, 1)) = 0
; no more frames
CloseFile(1)
ProcedureReturn #True
EndIf
; get frame size
FrameSize.l = 0
For Byte.l = 3 To 0 Step -1
FrameSize + (ReadByte(1) << (7*Byte))
Next
; add frame size to total size
Size + FrameSize
; skip flag and language bytes
ReadWord(1)
ReadByte(1)
; get frame contents
; subtract one for language byte
FrameSize - 1
Contents.s = Space(FrameSize)
ReadData(1,@Contents, FrameSize)
; put frame contents into structure
Select FrameID
Case "TALB"
TagID3v2\Album = Contents
Case "TCOM"
TagID3v2\Composer = Contents
Case "TCON"
TagID3v2\Genre = Contents
Case "TCOP"
TagID3v2\Copyright = Contents
Case "TENC"
TagID3v2\EncodedBy = Contents
Case "TIT2"
TagID3v2\Title = Contents
Case "TOPE"
TagID3v2\OrgArtist = Contents
Case "TPE1"
TagID3v2\Artist = Contents
Case "TRCK"
TagID3v2\Track = Contents
Case "TLEN"
TagID3v2\Length = Contents
Case "TSSE"
TagID3v2\Encoder = Contents
Case "TYER"
TagID3v2\Year = Contents
Case "COMM"
TagID3v2\Comment = Contents
Case "WXXX"
TagID3v2\URL = Contents
EndSelect
; stop if tag size reached/exceeded
Until Size >= ID3Size
CloseFile(1)
ProcedureReturn #True
EndProcedure
But I'm having problems with some (most) mp3 files, they make it crash...
I like logic, hence I dislike humans but love computers.
kenmo
Addict
Posts: 2032 Joined: Tue Dec 23, 2003 3:54 am
Post
by kenmo » Mon May 29, 2006 3:15 am
JC,
I wrote my own ID3 read/write routines a while back, and they are still in PB3 syntax so I won't bother to post them... but comparing yours and mine the only significant difference I see is that where you have
Code: Select all
ID3Size + (ReadByte(1) << (7*Byte))
I have
Code: Select all
ID3Size + ((ReadByte()&%01111111) << (7 * Byte))
and the same for FrameSize.
I actually don't remember what the bit masking is for, but I'm pretty sure it fixed my program from crashing almost 100%.
ETA - I just tried your procedure, found a mp3 that crashed, added my fix, and it works.
Joakim Christiansen
Addict
Posts: 2452 Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:
Post
by Joakim Christiansen » Mon May 29, 2006 3:30 am
kenmo wrote: JC,
I wrote my own ID3 read/write routines a while back, and they are still in PB3 syntax so I won't bother to post them... but comparing yours and mine the only significant difference I see is that where you have
Code: Select all
ID3Size + (ReadByte(1) << (7*Byte))
I have
Code: Select all
ID3Size + ((ReadByte()&%01111111) << (7 * Byte))
and the same for FrameSize.
I actually don't remember what the bit masking is for, but I'm pretty sure it fixed my program from crashing almost 100%.
ETA - I just tried your procedure, found a mp3 that crashed, added my fix, and it works.
Thank you kenmo!
But I will try to make my own anyway, just to learn.
I like logic, hence I dislike humans but love computers.
kenmo
Addict
Posts: 2032 Joined: Tue Dec 23, 2003 3:54 am
Post
by kenmo » Mon May 29, 2006 3:41 am
About that bitmasking, on the ID3 specifications on id3.org it reads
Code: Select all
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.
So if the MSB is supposed to be 0, I don't know why any mp3 encoder would set it to 1... and if the tag reader doesn't cancel that out, I believe the 1 on the MSB would make the length value negative or too large depending on whether or not it is signed... either way would explain the crashing.
KarLKoX
Enthusiast
Posts: 681 Joined: Mon Oct 06, 2003 7:13 pm
Location: France
Contact:
Post
by KarLKoX » Tue May 30, 2006 1:18 am
Code: Select all
Procedure.l GetSizeV2(fichier.s)
Protected m_taille.l, FIO.l
Dim m_byte.b(3)
FIO = ReadFile(#Pb_Any,fichier)
if FIO <= 0
procedurereturn 0
endif
FileSeek(FIO,6)
ReadData(FIO,@m_byte(), 3)
m_taille = (m_byte(0) << 21) | (m_byte(1) << 14) | (m_byte(2) << 7) | m_byte(3)
; Add the size of the frame header
m_taille + 10
closefile(FIO)
procedurereturn m_taille
EndProcedure
http://sbougribate.free.fr/Files/PureBasic/IDTags.pb
kenmo
Addict
Posts: 2032 Joined: Tue Dec 23, 2003 3:54 am
Post
by kenmo » Tue May 30, 2006 5:00 pm
Don't forget to check for the ID3 $03 header though...
I just tried that on a mp3 without ID3 and I got a negative result.