Wiele razy byłem pytany o to, jak odczytać z pliku mp3 ID3TAGv2. Odczytanie ID3TAGv1 jest niezmiernie proste, a kod znajduje się gdzieś na 4p.
Natomiast z ID3TAGv2 jest trochę trudniej.
Najpierw należy się przyjrzeć ich budowie. W wersji pierwszej, jeżeli tag jest zapisany, wszystkie jego pola są zapisane, nawet jeżeli są puste. I tu łatwa sprawa... Natomiast w wersji drugiej, nie muszą występować wszystkie pola. A pola puste w ogóle nie występują...
Teraz co nieco o budowie wersji drugiej.
W pierwszych trzech bajtach pliku powinien być identyfikator: 'ID3'. Jeżeli go nie ma, znaczy to, że tag nie jest zapisany.
No więc tak. TAG jest, więc co dalej?
Ano nic prostszego, po prostu go odczytać :)
Po identyfikacji tagu jest 7 bajtów, które nas nie interesują.
Pierwsze pole tagu zaczyna się w 11 bajcie.
I teraz tak. Ze względu na to, że nie wszystkie pola tagu muszą istnieć, autorzy tagu postarali się jakby o identyfikatory pól. I teraz będzie o polach, jakie zawiera wersja druga:
Identyfikator| Nazwa
TENC | dekoder mp3
WXXX | adres www
TCOP | prawa autorskie
TOPE | oryginalny wykonawca
TCOM | kompozytor
COMM | komentarz
TYER | rok
TMED | nie mam pojęcia, do czego to jest, ale nigdy tego nie używałem :)
TCON | rodzaj muzyki
TRCK | numer ścieżki w playliście
TALB | album
TPE1 | artysta
TIT2 | tytuł
Jak widać PRAWIE wszystkie identyfikatory pól rozpoczynają się literką T, a wszystkie mają długość 4 bajtów. Więc skąd wiedzieć, co jest czym?
Po prostu musimy stworzyć sobie tablicę stałych, która może wyglądać np. tak:
const
id: array[0..13] of string = ('TENC', 'WXXX', 'TCOP', 'TOPE', 'TCOM',
'COMM', 'TYER', 'TMED', 'TCON', 'TLEN', 'TRCK', 'TALB', 'TPE1', 'TIT2');
I teraz jeszcze jedna ważna rzecz, o której muszę powiedzieć...
Budowa tego tagu wygląda mniej - więcej tak:
identyfikator tagu + 7 bajtów 'wolnych'+identyfikator pola+ 7(8, 11) bajtów wolnych+ identyfikator następnego pola + 7(12) bajtów wolnych....
Przykład takiego tagu: (kropką oznaczam wolne bajty)
ID3.......TENC.......DekoderWXXX........Adres wwwTCOP.......prawa autorskieTPE1.......ArtystaTIT2.......tytułTALB.......AlbumCOMM...........Komentarz
Więc tak jak widać, identyfikator kolejnego pola zaczyna się w następnym bajcie po ostatnim polu.
Teraz kolejna ważna rzecz...
Między niektórymi identyfikatorami pól, a ich 'zawartością' jest 7, 8 lub 11 bajtów wolnych. Teraz to zapiszę...
Między identyfikatorem TENC, a jego zawartością(czyli w tym wypadku 'Dekoder'), jest 7 bajtów wolnych.
Między identyfikatorem WXXX, a jego zawartością jest 8 bajtów wolnych.
Między identyfikatorem COMM, a jego zawartością jest 11 bajtów wolnych.
Między resztą identyfikatorów, a ich zawartością jest 7 bajtów wolnych.
Więc teraz krok drugi do odczytania tagu (krokiem pierwszym była ta tablica stałych)
Tworzymy typ rekordowy, np:
type
TId3TAGv2 = record
ID: string;
Tytul, Artysta, Album, Rok, Komentarz: string;
encoder, url, prawa, kompozytor, oryginalny: string;
end;
I następnie po prostu odczytujemy po kolei 4 bajty z pliku i sprawdzamy, czy ich zawartość znajduje się w naszej tablicy stałych...
Mam nadzieję, że wszyscy pokapowali o co chodzi.... A oto przykładowy kod autorstwa mojego do odczytu TAGUv2:
(*********************************************)
(* Odczytu ID3TAGv2 z pliku mp3 *)
(* autor: Adam 'Juhas' Jachocki *)
(* e-mail: [email protected] *)
(* www.juhas.glt.pl *)
(*********************************************)
const
id: array[0..13] of string = ('TENC', 'WXXX', 'TCOP', 'TOPE', 'TCOM',
'COMM', 'TYER', 'TMED', 'TCON', 'TLEN', 'TRCK', 'TALB', 'TPE1', 'TIT2');
type
TId3TAGv2 = record
ID: string;
Tytul, Artysta, Album, Rok, Komentarz: string;
encoder, url, prawa, kompozytor, oryginalny: string;
end;
var
tag2: TID3Tagv2;
procedure Tform1.TAG2Open(nazwaPliku: string);
var
f: TfileStream;
buffer: array[1..512] of char;
buffer2: array[1..3] of char;
enc, url, cop, ope, com, kom, yer, rck, alb, tit, tpe: string; //wartości
SprId, sprId2, wartosc: string; //sprawdza, po odczytaniu 4 znaków, czy
SprID jest w ID
p, d, wartosc1, wartosc2: integer;
//p - znak ewentualnego Identyfikatora
//d - drugi ewentualny identyfikator
begin
fileMode:=0;
f:=TFileStream.Create(nazwaPliku, fmOpenRead);
try
f.readBuffer(buffer2, 3);
finally
f.Free;
end;
//tutaj trzeba te pola 'wyzerować'
tit:='';
tpe:='';
alb:='';
yer:='';
kom:='';
enc:='';
url:='';
cop:='';
com:='';
ope:='';
tag2.ID:=buffer2;
if buffer2='ID3' then begin
f:=TfileStream.Create(nazwaPliku, fmOpenRead);
try
f.ReadBuffer(buffer, 512);
finally
f.Free;
end;
p:=1;
d:=0;
for bufor:=1 to 512 do begin
if bufor=512 then begin
wartosc1:=p+5;
wartosc:=copy(buffer, wartosc1, bufor);
if sprID='TENC' then enc:=wartosc;
if sprID='WXXX' then url:=wartosc;
if sprID='TCOP' then cop:=wartosc;
if sprID='TOPE' then ope:=wartosc;
if sprID='TCOM' then com:=wartosc;
if sprID='COMM' then kom:=wartosc;
if sprID='TYER' then yer:=wartosc;
if sprID='TALB' then alb:=wartosc;
if sprID='TIT2' then tit:=wartosc;
if sprID='TPE1' then tpe:=wartosc;
end;
if (buffer[bufor]='T') or (buffer[bufor]='C') or (buffer[bufor]='W') then
begin
if p=1 then p:=bufor
else d:=bufor;
if p=bufor then begin
sprId:=buffer[bufor]+buffer[bufor+1]+buffer[bufor+2]+buffer[bufor+3];
if (sprId<>id[0]) and (sprID<>id[1]) and (sprId<>id[2]) and
(sprID<>id[3]) And (sprID<>id[4]) and (sprID<>id[5]) AND (sprID<>id[6]) And
(sprID<>id[7]) and (sprID<>id[8]) and (sprID<>id[9]) and (sprID<>id[10]) and
(sprID<>id[11]) and (sprID<>id[12]) and (sprID<>id[13]) then begin
d:=0;
p:=1;
end else sprID:=sprID;
end;
if p<>bufor then begin
sprId2:=buffer[bufor]+buffer[bufor+1]+buffer[bufor+2]+buffer[bufor+3];
if (sprId2=id[0]) or (sprID2=id[1]) or (sprId2=id[2]) or
(sprID2=id[3]) or (sprID2=id[4]) or (sprID2=id[5]) or (sprID2=id[6]) or
(sprID2=id[7]) or (sprID2=id[8]) or (sprID2=id[9]) or (sprID2=id[10]) or
(sprID2=id[11]) or (sprID2=id[12]) or (sprID2=id[13]) then begin
wartosc1:=p+11;
wartosc:=copy(buffer,
wartosc1, d-wartosc1);
if sprID='TENC' then
enc:=wartosc;
if sprID='WXXX' then
url:=wartosc;
if sprID='TCOP' then
cop:=wartosc;
if sprID='TOPE' then
ope:=wartosc;
if sprID='TCOM' then
com:=wartosc;
if sprID='COMM' then
kom:=wartosc;
if sprID='TYER' then
yer:=wartosc;
if sprID='TALB' then
alb:=wartosc;
if sprID='TIT2' then
tit:=wartosc;
if sprID='TPE1' then
tpe:=wartosc;
sprID:=sprID2;
p:=d;
d:=0;
end else d:=0;
end;
end;
end;
end;
tag2.Tytul:=trim(tit);
tag2.Artysta:=tpe;
tag2.Album:=alb;
tag2.Rok:=yer;
tag2.Komentarz:=trim(kom);
tag2.encoder:=enc;
tag2.url:=trim(url);
tag2.prawa:=cop;
tag2.kompozytor:=com;
tag2.oryginalny:=ope;
end;
Ten kod może być nieco niezrozumiały, ale działa :)
Wszelkie pytania kierujcie do mnie.
Dopiero teraz to zamieszczasz. A ja już zrobiłem program
www.4programmers.net/download.php?id=889
i będę musiał przerobić.
Ten kod działa, ale na ID3v2 0.2, czyli z resztą jest nie kompatybilny (choć może czasem) należy równierz pamietać że ID3v2 może być wielojęzykowy, czyli WideChar oraz może mieć maksymalnie 256MB! objętości nie licząc samej zawartości plików mp3
< delphi > i </ delphi >
za to masz - 3
Niedługo pewnie umieszczę zupełnie inny kod już taki bardziej profesjonalny(zapisywanie też będzie uwzględnione). Jak tylko będzie mi się chciało go dokończyć ;)
a to wejdzie na ID3TAGv1 i na ID3TAGv2 czy tylko na ID3TAGv2??
W kodzie programu w artykule sa bledy ... i program nalezy poprawic zeby dzialal.
Zamiast zawilej procedury poszukiwania taga proponuje sprawe uproscic:
type TTagBuffer= array[1..1024] of char; // sam nie wiem jaka jest poprawny
//rozmiar ale to powinno wystarczyc
procedure TForm1.FindTagPos(AName: string; ABuffer: TTagBuffer; var AStart, ALength: cardinal);
var k: integer;
begin
AStart:=Pos(AName, ABuffer);
if AStart>0 then
begin
inc(AStart,length(AName));
ALength:= byte(ABuffer[AStart]); //odczytaj dlugosc taga
k:=AStart+3;
while AStart<k do
begin
inc(AStart);
ALength:= ALength shl 8 or byte(ABuffer[AStart]);
end;
inc(AStart,4);
dec(ALength);
if AName='WXXX' then inc(AStart);
end;
end;