'After writing ID3v1.1 decoder for Qb64 I wrote this decoder ID3TAGV2.3.0 for QB64. I want in first thank Clippy for sharing his program, which dealt with the same thing in another way.
'Hardest of all was to understand how the author ID3TAG thinks with the calculation of the size HEAD in binary code. If he wrote it straight - sort by itself without the eighth bit and convert to decimal form,
'probably would have consumed much paper. That gave me the most work.
'this is extended version. Program first use V2.3.0 program. IF this ends with error (no record found), is automatically runned upgraded version ID3V1.1. Only when one can not find even a record
'identifier TAG, then the program will report that in the file is none ID3 and ends. Read NOT ID3V2.2 (V2.2 is with 3 characters indetificator), but read ID3V2.4
_TITLE "ID3V2.3 PLUS ID3V1.1 (V1.1 upgraded) Reader" INPUT "Input MP3 filename without extension to view ID3:"; file$
file$ = file$ + ".MP3"
'file$ = "14.mp3" 's.mp3 hav e APIC.
VersionMajor
AS STRING * 1 ' Classical way - ASC number of this characters in file is Version number
DIM OUTList
(150) AS STRING * 80 ' output list with infos from ID3TAG
IF ID3.Identifier$
<> "ID3" THEN PRINT "ID3 mark not found": IDENTIT$
= "ID3V1.1":
GOTO ID3V11
' BEEP: PRINT ID3.Identifier$: SLEEP: SYSTEM IDENTIT$ = Version$
PRINT "Binaries (bites) in FLAGS: ";:
PRINT DECtoBIN$
(ID3.BinarFlags
);:
PRINT " HEX: ";:
PRINT BINtoHEX$
(DECtoBIN$
(ID3.BinarFlags
)) Ba$
= DECtoBIN$
(ASC(ID3.BinarSizeA$
))Bb$
= DECtoBIN$
(ASC(ID3.BinarSizeB$
)) ' FUNCTION converted decimal numbers to binar numbersBc$
= DECtoBIN$
(ASC(ID3.BinarSizeC$
))Bd$
= DECtoBIN$
(ASC(ID3.BinarSizeD$
))
PRINT "Binaries (bites) in SizeRecordA Byte: "; Ba$;:
PRINT " HEX: ";:
PRINT HEX$(ASC(ID3.BinarSizeA$
));:
PRINT " DEC: "; ID3.BinarSizeA$;
" (";
ASC(ID3.BinarSizeA$
);
")" PRINT "Binaries (bites) in SizeRecordB Byte: "; Bb$;:
PRINT " HEX: ";:
PRINT HEX$(ASC(ID3.BinarSizeB$
));:
PRINT " DEC: "; ID3.BinarSizeB$;
" (";
ASC(ID3.BinarSizeB$
);
")" ' It is such a pleasantry PRINT "Binaries (bites) in SizeRecordC Byte: "; Bc$;:
PRINT " HEX: ";:
PRINT HEX$(ASC(ID3.BinarSizeC$
));:
PRINT " DEC: "; ID3.BinarSizeC$;
" (";
ASC(ID3.BinarSizeC$
);
")" PRINT "Binaries (bites) in SizeRecordD Byte: "; Bd$;:
PRINT " HEX: ";:
PRINT HEX$(ASC(ID3.BinarSizeD$
));:
PRINT " DEC: "; ID3.BinarSizeD$;
" (";
ASC(ID3.BinarSizeD$
);
")" h = HEAD(headd$) / 2
PRINT "Head size calculated AS STRING: "; h;
" bytes"
IF VAL(RIGHT$(LEFT$(DECtoBIN$
(ID3.BinarFlags
), 1), 1)) = 0 THEN PRINT "Extended header for ID3TAG is not used" ELSE PRINT "Extended header for ID3TAG is used - not supported by this program" IF VAL(RIGHT$(LEFT$(DECtoBIN$
(ID3.BinarFlags
), 2), 1)) = 1 THEN PRINT "Experimental ID3 TAG!" 'VAL is possile to use because strings contains zero or one
'----------- cteni framu -------------- frames reading
w = 0 ' FRAME head is 10 bytes long. 4 byte = name "AENC" or other, 4 byte size, 2 byte flags. Size is calculated as ASC sum of all four
home: ' bytes.
GET #1, , FRM
' hlava ma 10 bytu: 4 byty jmeno, 4 byty velikost, 2 byty flags. Vse je psano klasicky hexadecimalne, uz zadny nesmysly s bitama. ' minimalni krok k dalsimu zaznamu ma byt 11 bytu (1byt je minimum pro kazdy identifikator, 10 byt
' je velikost hlavy. Velikost framu je dana souctem ASC ctyr bytu v hlave Framu.
FrameSize
= ASC(FRM.SizeA$
) + ASC(FRM.SizeB$
) + ASC(FRM.SizeC$
) + ASC(FRM.SizeD$
)InFrame$
= SPACE$(FrameSize
) 'this statement using is Clippy method, thank Clippy!q$ = FRM.Id$
SELECT CASE FRM.Id$
' - - - - - - - - - - - - - - - - X = HAVE OWN SECOND HEAD and can be more defined CASE "AENC": q$
= "Audio encryption:" ' X CASE "APIC": q$
= "Attached Picture:": APICSUB:
GOTO home
'X CASE "COMM": q$
= "Comments:" ' X CASE "COMR": q$
= "Commercial frame:" ' X CASE "ENCR": q$
= "Encryption method:" ' X CASE "EQUA": q$
= "Equalization:" ' X CASE "ETCO": q$
= "Event timing codes:" ' X CASE "GEOB": q$
= "General encapsulated object:" ' X CASE "GRID": q$
= "Group identification registration:" ' X CASE "IPLS": q$
= "Involved people list:" CASE "LINK": q$
= "Linked information:" ' X CASE "MCDI": q$
= "Music CD identifier:" ' X CASE "MLLT": q$
= "MPEG location lookup table:" ' X CASE "OWNE": q$
= "Ownership frame:" ' X CASE "PRIV": q$
= "Private frame:" ' X CASE "PCNT": q$
= "Play counter:" ' X CASE "POPM": q$
= "Popularimeter:" CASE "POSS": q$
= "Position synchronisation frame:" ' X CASE "RBUF": q$
= "Recommended buffer size:" ' X CASE "RVAD": q$
= "Relative volume adjustment:" ' X CASE "RVRB": q$
= "Reverb" ' X CASE "SYLT": q$
= "Synchronized lyric / text:" CASE "SYTC": q$
= "Synchronized tempo codes:" ' X CASE "TALB": q$
= "Album / Movie / Show title:" CASE "TBPM": q$
= "Beats per minute:" CASE "TCOM": q$
= "Composer: " CASE "TCON": q$
= "Content type:" ' X - number = style as V1.1 CASE "TCOP": q$
= "Copyright message:" CASE "TDAT": q$
= "Date:" ' numeric record always 4 bytes long CASE "TDLY": q$
= "Playlist delay:" CASE "TENC": q$
= "Encoded by:" CASE "TEXT": q$
= "Lyricist / Text Writer" CASE "TFLT": q$
= "File type:" ' X - 1/2/3/2.5(MPGs)/AAC/VQF/PCM CASE "TIME": q$
= "Time:" ' format HH:MM CASE "TIT1": q$
= "Content group description:" CASE "TIT2": q$
= "Title / songname:" CASE "TIT3": q$
= "Subtitle / Description refinement:" CASE "TKEY": q$
= "Initial key:" CASE "TLAN": q$
= "Language:" CASE "TLEN": q$
= "Length:" CASE "TMED": q$
= "Media type" ' X CASE "TOAL": q$
= "Original album / movie / show title:" CASE "TOFN": q$
= "Original filename:" CASE "TOLY": q$
= "Original lyricist / text writer:" CASE "TOPE": q$
= "Original artist / performer:" CASE "TORY": q$
= "Original release year:" CASE "TOWN": q$
= "File owner / license:" CASE "TPE1": q$
= "Lead performer / Soloist:" CASE "TPE2": q$
= "Band / orchestra / accompaniment:" CASE "TPE3": q$
= "Conductor / performer refinement:" CASE "TPE4": q$
= "Modified by:" CASE "TPOS": q$
= "Part of a set:" CASE "TPUB": q$
= "Publisher:" CASE "TRCK": q$
= "Track number / Position in set:" CASE "TRDA": q$
= "Recording dates:" CASE "TRSN": q$
= "Internet radio station name:" CASE "TRSO": q$
= "Internet radio station owner:" CASE "TSIZ": q$
= "Size:" CASE "TSRC": q$
= "International standard recording code (ISRC):" CASE "TSSE": q$
= "Software / Hardware used for encoding:" CASE "TYER": q$
= "Year:" CASE "TXXX": q$
= "User defined text frame:" ' X - text encoding, description and value CASE "UFID": q$
= "Unique file identifier:" CASE "USER": q$
= "Terms of use:" ' X CASE "USLT": q$
= "Unsychronized lyric/text transcription:": USLTSUB
'X CASE "WCOM": q$
= "Commercial information:" CASE "WCOP": q$
= "Copyright / Legal information:" CASE "WOAF": q$
= "Official audio file webpage:" CASE "WOAR": q$
= "Official artist / performer webpage:" CASE "WOAS": q$
= "Official audio source webpage:" CASE "WORS": q$
= "Official internet radio webpage:" CASE "WPAY": q$
= "Payment:" CASE "WPUB": q$
= "Publishers official webpage:" CASE "WXXX": q$
= "User defined URL link frame:" ' X 'GET #1, , InFrame$ ' first muss i analyzing metadata
PRINT textencod1
, textencod2
OUTList$(w) = q$ + InFrame$
PRINT "Frame: "; FRM.Id$;
" Frame size: "; FrameSize;
" and record in this frame: "; InFrame$
InFrame$ = ""
readet = readet + 10 + FrameSize
'PRINT readet, FrameSize
EndRec:
PRINT "Press any key to end"
ID3V11: 'upgraded ID3V1.1 program (upgraded in end file access, many better)
GET #1, , re0$:
IF LEFT$(re0$
, 3) <> "ID3" THEN PRINT "ID3 mark not found! - but i try this" ' its opened, also its posibble to have uncorrect outputs. 'UPGRADE ////
IF e
< 128 THEN PRINT "Error: LOF returned file length < 128 bytes!" SEEK #1, e
'better and many faster access i am still learning 'UPGRADE END
re$ = re0$ + re2$ ' After several attempts, I realized that the begin of the last record may not be the very last recording at the end of the file... :-D
'PRINT re$, e: SLEEP
FOR scan
= 1 TO LEN(re$
) ' Here this loop byte to byte scanned re$ for text "TAG" - its definition for ID3 tag begin IF id$
= "TAG" THEN sca
= scan: found
= 1 ' byte position in string, "found" is myself method to prevent uncorrect outputs if file have none or ID3 V.2.2 ID3TAG, but its not usefull at 100% dal:
'COLOR , 5 i have here tested long text arrays
Autor$
= LEFT$(MID$(re$
, 33 + sca
), 30) ' filtering stringsAlbum$
= LEFT$(MID$(re$
, 63 + sca
), 30) ' This way "integer = ASC(LEFT$(MID$(string$, position), long)) is way how read MP3 HEAD. BUT MP3 HEAD contains MANY tables and recordings.none:
'PRINT re$ ' If you delete this mark "'", you see as its writed in file
PRINT "Song name: "; SongName$
' Song PRINT "Author: "; Autor$
' Author name PRINT "Album: "; Album$
' Album PRINT "Year: "; Rok$
' Year PRINT "Comment: "; Coment$
' Comment PRINT "Track: "; track$
' Track number PRINT "Genre: "; genre$
' Genre genre:
DATA 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
,Eurotechno
,Ambient
DATA 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
DATA Gothic
,Darkwave
,Techno
-Industrial
,Electronic
,Jungle
,Pop
-Folk
,Eurodance
,Dream
,Southern Rock
,Comedy
,Cult
,Gangsta
,Top
40,Christian Rap
,Pop
/Funk
,Native American
,Cabaret
,New Wave
,Psychadelic
,Rave
,Show Tunes
DATA Trailer
,Lo
-Fi
,Tribal
,Acid Punk
,Acid Jazz
,Polka
,Retro
,Musical
,Rock & Roll
,Hard Rock
,Folk
,Folk
/Rock
,National Folk
,Swing
,Fast
-Fusion
,Bebop
,Latin
,Revival
,Celtic
,Bluegrass
,Avantgarde
,Gothic Rock
,Progressive Rock
DATA 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
DATA Folklore
,Ballad
,Power Ballad
,Rhytmic Soul
,Freestyle
,Duet
,Punk Rock
,Drum Solo
,Acapella
,Euro
-House
,Dance Hall
,Goa
,Drum & Bass
,Club
-House
,Hardcore
,Terror
,Indie
,BritPop
,Negerpunk
,Polsk Punk
,Beat
,Christian Gangsta Rap
DATA Heavy Metal
,Black Metal
,Crossover
,Contemporary Christian
,Christian Rock
' HAPPY CODING!
'FOR a = 1 TO LEN(InFrame$)
'r$ = LEFT$(MID$(InFrame$, a), 1)
'PRINT a, r$, ASC(r$)
'SLEEP
'NEXT a
SHARED InFrame$
, a
, h
' h is ID3TAGV2.3.0 size pokusnavelikost
= h
- SEEK(1)
' FOR a = 1 TO LEN(InFrame$)
' r$ = LEFT$(MID$(InFrame$, a), 1)
' PRINT a, r$, ASC(r$)
' SLEEP
' NEXT a ' LADENI (program setup)
PRINT "MIME TYPE:"; mime$
PRINT "TEXT:"; te$;
"DELKA inframe$ je: (primo bez odecteni): ";
LEN(InFrame$
);
"jsi na pozici:";
SEEK(1) ext$ = ".jpg"
'GOTO pokus
CASE "image/jpe": ext$
= ".jpg":
GOTO POKUS
'k.mp3 CASE "image/jpg": ext$
= ".jpg" 'c++ source
'HERE is comming a very BIG problem for me: HOW TO FIND PICTURE SIZE? All sources was i can are bad. Also i use one old method - because record in image head is unussable - bytes with size are bad - or
'WIKI specification is not correct, author ID3 write about this nothing and frame lenght is bad, i muss scanning file for next idetificator after "APIC". This write to array and the one that is closest
'to me precisely determines the length of the picture. Although this is smut, but i do not know how else to achieve results - if cant right bytes with size.
'if you need repairing me, so: image is MIME format and this is NOT JPEG! its other format with the same name.
Identity$
= LEFT$(MID$(HYPERBLOCK$
, D4FF
), 4) IF Identity$
= IDENTIT$
THEN GOTO VOALA
' First correct detection ends this loop and then with PEEK i calculate correct image size ' LOCATE 13, 5: PRINT LOF(1), "/", FATALITY, IDENTIT$, iScan, Identity$
EXIT SUB 'next record not found...views outputs and ends
Size = new - oll
'PRINT "LOOPSIZE", Size, new, oll, IDENTIT$: BEEP: SLEEP
Size = Size + 64
REM /////////////////////////////// POKUS:
Size = pokusnavelikost
REM //////////////////////////////
mega$
= SPACE$(Size
) 'if here is correct size, its maked right copy of image to disc.'InFrame$ = ""
IDENTIT:
DATA AENC
,APIC
,COMM
,COMR
,ENCR
,EQUA
,ETCO
,GEOB
,GRID
,IPLS
,LINK
,MCDI
,MLLT
,OWNE
,PRIV
,PCNT
,POPM
,POSS
,RBUF
,RVAD
,RVRB
,SYLT
,SYTC
,TALB
,TBPM
,TCOM
,TCON
,TCOP
,TDAT
,TDLY
,TENC
,TEXT
,TFLT
,TIME
,TIT1
,TIT2
,TIT3
DATA TKEY
,TLAN
,TLEN
,TMED
,TOAL
,TOFN
,TOLY
,TOPE
,TORY
,TOWN
,TPE1
,TPE2
,TPE3
,TPE4
,TPOS
,TPUB
,TRCK
,TRDA
,TRSN
,TRSO
,TSIZ
,TSRC
,TSSE
,TYER
,TXXX
,UFID
,USER
,USLT
,WCOM
,WCOP
,WOAF
,WOAR
,WOAS
,WORS
,WPAY
,WPUB
,WXXX
'Although it works, but if no other identifiers after APIC file, so it will end up in long loop. So if this view, then, alone with previous tests of that there is a trailing support, the identifier
'or simply not show. It's stupid, but a master programmer ID3 tag is inherently inconsistent to man. Head ID3 tag fits binary level, and here size does not deliver. That would put a medal.
'co taky chtit od svedu. Maximalne ten Hulmiho Ukolen by jeste sel...
FUNCTION HEAD
(b
AS STRING) 'BIN to DEC vystup je integer, vstup je string (jmeno FUNKCE je promenna s hodnotou z funkce) DECtoBI2 = DECtoBI2 + (c * 2 ^ Sj)
HEAD = DECtoBI2
'---- FUNCTIONs TEST ---- if is this copyed alone as new program
'b$ = DECtoBIN$(255)
'b = BINtoDEC(b$)
'c$ = HEX$(b)
'c = HEXtoDEC(c$)
'PRINT b$, b, c$, c
FUNCTION DECtoBIN$
(vstup
) 'DEC to BIN ok vystup je string, vstup je integer decimal to binar number convertor - FROM QB64WIKI ' BINARY$ = ""
IF vstup
AND 2 ^ rj
THEN BINtoDE$
= BINtoDE$
+ "1" ELSE BINtoDE$
= BINtoDE$
+ "0" DECtoBIN$ = BINtoDE$
FUNCTION BINtoDEC
(b
AS STRING) 'BIN to DEC vystup je integer, vstup je string (jmeno FUNKCE je promenna s hodnotou z funkce) c
= VAL(e$
) ' binar to decimal number convertor Sj = 7 - Si
DECtoBI = DECtoBI + (c * 2 ^ Sj)
BINtoDEC = DECtoBI
FUNCTION HEXtoDEC
(h
AS STRING) 'Vystup je integer, vstup je string, opacne k funkci HEX$ HEXtoDEC
= VAL("&H" + h$
) ' hexadecimal to decimal number convertor
c = BINtoDEC(bi$) ' binar to hexadecimal number convertor (use binar to decimal convertor)