Консультация № 140933
28.07.2008, 22:25
0.00 руб.
0 3 0
Уважаемые Эксперты!
Помогите мне пожалуйста! Я начал изучать Дельфи, и уже немного ее освоил. Заинтересовался редактированием ID3v2 mp3 на Дельфи. Нашел в Сети исходный код и пользуюсь им. Тэг используется как компонент. Все хорошо, только я хотел разобраться как это работает, а заодно и добавить что-нибудь свое (например, в данном исходном коде нет функции редактирования изображения в mp3). Помогите, пожалуйста! Заранее благодарен.
В приложении только часть кода, потому что туда больше 12 000 символов не влезет. Остальное пришлю.

Приложение:
unit ID3v2;
interface
uses
Classes,SysUtils;
const
TAG_VERSION_2_2=2;
TAG_VERSION_2_3=3;
TAG_VERSION_2_4=4;
type
TID3v2=class(TObject)
private
FExists:boolean;
FVersionID:byte;
FSize:integer;
FTitle:string;
FArtist:string;
FAlbum:string;
FTrack:word;
FTrackString: string;
FYear:string;
FGenre:string;
FComment:string;
FComposer:string;
FEncoder:string;
FCopyright:string;
FLanguage:string;
FLink:string;
FTSIZ:string;
procedure FSetTitle(const NewTitle:string);
procedure FSetArtist(const NewArtist:string);
procedure FSetAlbum(const NewAlbum:string);
procedure FSetTrack(const NewTrack:word);
procedure FSetYear(const NewYear:string);
procedure FSetGenre(const NewGenre:string);
procedure FSetComment(const NewComment:string);
procedure FSetComposer(const NewComposer:string);
procedure FSetEncoder(const NewEncoder:string);
procedure FSetCopyright(const NewCopyright:string);
procedure FSetLanguage(const NewLanguage:string);
procedure FSetLink(const NewLink:string);
public
constructor Create;
function ReadFromFile(const FileName: WideString):boolean;
function SaveToFile(const FileName: WideString):boolean;
function RemoveFromFile(const FileName: WideString):boolean;
property Exists:boolean Read FExists;
property VersionID:byte Read FVersionID;
property Size:integer Read FSize;
property Title:string Read FTitle Write FSetTitle;
property Artist:string Read FArtist Write FSetArtist;
property Album:string Read FAlbum Write FSetAlbum;
property Track:word Read FTrack Write FSetTrack;
property TrackString:string Read FTrackString;
property Year:string Read FYear Write FSetYear;
property Genre:string Read FGenre Write FSetGenre;
property Comment:string Read FComment Write FSetComment;
property Composer:string Read FComposer Write FSetComposer;
property Encoder:string Read FEncoder Write FSetEncoder;
property Copyright:string Read FCopyright Write FSetCopyright;
property Language:string Read FLanguage Write FSetLanguage;
property Link:string Read FLink Write FSetLink;
property TSIZ:string Read FTSIZ;
end;

implementation

const
ID3V2_ID='ID3';
ID3V2_FRAME_COUNT=17;
ID3V2_FRAME_NEW:array [1..ID3V2_FRAME_COUNT] of string=
('TIT2','TPE1','TALB','TRCK','TYER','TCON','COMM','TCOM','TENC',
'TCOP','TLAN','WXXX','TDRC','TOPE','TIT1','TOAL','TSIZ');
ID3V2_FRAME_OLD:array [1..ID3V2_FRAME_COUNT] of string=
('TT2','TP1','TAL','TRK','TYE','TCO','COM','TCM','TEN',
'TCR','TLA','WXX','TOR','TOA','TT1','TOT','TSI');
ID3V2_MAX_SIZE=4096;

UNICODE_ID=#1;

type

FrameHeaderNew=record
ID:array [1..4] of char;
Size:integer;
Flags:word;
end;

FrameHeaderOld=record
ID:array [1..3] of char;
Size:array [1..3] of byte;
end;
TagInfo = record
ID:array [1..3] of char;
Version: byte;
Revision: byte;
Flags: byte;
Size: array [1..4] of byte;
FileSize: integer;
Frame: array [1..ID3V2_FRAME_COUNT] of string;
NeedRewrite: boolean;
PaddingSize: integer;
end;
function ReadHeader(const FileName: WideString; var Tag: TagInfo): boolean;
var
SourceFile: TFileStream;
Transferred: integer;
begin
try
Result:= True;
SourceFile:=TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
Transferred:=SourceFile.Read(Tag, 10);
Tag.FileSize:=SourceFile.Size;
SourceFile.Free;
if Transferred < 10 then
Result:=False;
except
Result:=False;
end;
end;

function GetTagSize(const Tag:TagInfo):integer;
begin
Result :=
Tag.Size[1]*$200000+Tag.Size[2]*$4000+Tag.Size[3]*
$80+Tag.Size[4]+10;
if Tag.Flags and $10=$10 then
Inc(Result, 10);
if Result>Tag.FileSize then
Result:= 0;
end;

procedure SetTagItem(const ID,Data:string;var Tag:TagInfo);
var
Iterator:byte;
FrameID:string;
begin
for Iterator:=1 to ID3V2_FRAME_COUNT do
begin
if Tag.Version>TAG_VERSION_2_2 then
FrameID:=ID3V2_FRAME_NEW[Iterator]
else
FrameID:=ID3V2_FRAME_OLD[Iterator];
if(FrameID=ID)and(Data[1]<=UNICODE_ID) then
Tag.Frame[Iterator]:=Data;
end;
end;

function Swap32(const Figure:integer):integer;
var
ByteArray: array [1..4] of byte absolute Figure;
begin
Result:=
ByteArray[1]*$1000000+ByteArray[2]*$10000+ByteArray[3]*
$100+ByteArray[4];
end;

procedure ReadFramesNew(const FileName:WideString;var Tag:TagInfo);
var
SourceFile:TFileStream;
Frame:FrameHeaderNew;
Data:array [1..500] of char;
DataPosition,DataSize:integer;
begin
try
SourceFile:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
SourceFile.Seek(10,soFromBeginning);
while(SourceFile.Position<GetTagSize(Tag))and
(SourceFile.Position<SourceFile.Size)do
begin
FillChar(Data,SizeOf(Data),0);
SourceFile.Read(Frame,10);
if not(Frame.ID[1] in ['A'..'Z']) then
break;
DataPosition:=SourceFile.Position;
if Swap32(Frame.Size)>SizeOf(Data) then
DataSize:=SizeOf(Data)
else
DataSize:=Swap32(Frame.Size);
SourceFile.Read(Data,DataSize);
if Frame.Flags and $8000<>$8000 then
SetTagItem(Frame.ID,Data,Tag);
SourceFile.Seek(DataPosition+Swap32(Frame.Size),soFromBeginning);
end;
SourceFile.Free;
except
end;
end;

Обсуждение

Неизвестный
28.07.2008, 22:27
общий
{А это - остальная часть кода}
procedure ReadFramesOld(const FileName:WideString;var Tag:TagInfo);
var
SourceFile:TFileStream;
Frame:FrameHeaderOld;
Data:array [1..500] of char;
DataPosition,FrameSize,DataSize:integer;
begin
try
SourceFile:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
SourceFile.Seek(10,soFromBeginning);
while (SourceFile.Position<GetTagSize(Tag)) and
(SourceFile.Position<SourceFile.Size) do
begin
FillChar(Data,SizeOf(Data),0);
SourceFile.Read(Frame,6);
if not (Frame.ID[1] in ['A'..'Z']) then
break;
DataPosition:=SourceFile.Position;
FrameSize:= Frame.Size[1] shl 16+Frame.Size[2] shl 8+Frame.Size[3];
if FrameSize>SizeOf(Data) then
DataSize:=SizeOf(Data)
else
DataSize:=FrameSize;
SourceFile.Read(Data,DataSize);
SetTagItem(Frame.ID,Data,Tag);
SourceFile.Seek(DataPosition+FrameSize,soFromBeginning);
end;
SourceFile.Free;
except
end;
end;

function GetANSI(const Source:string):string;
var
Index:integer;
FirstByte,SecondByte:byte;
UnicodeChar:widechar;
begin
if (Length(Source)>0) and (Source[1]=UNICODE_ID) then
begin
Result:='';
for Index:=1 to ((Length(Source) - 1) div 2) do
begin
FirstByte:=Ord(Source[Index * 2]);
SecondByte:=Ord(Source[Index * 2 + 1]);
UnicodeChar:=widechar(FirstByte or (SecondByte shl 8));
if UnicodeChar=#0 then
break;
if FirstByte<$FF then
Result:=Result+UnicodeChar;
end;
Result:=Trim(Result);
end
else
Result:=Trim(Source);
end;

function GetContent(const Content1,Content2:string):string;
begin
Result:=GetANSI(Content1);
if Result='' then
Result:=GetANSI(Content2);
end;

function ExtractTrack(const TrackString:string):word;
var
Track:string;
Index,Value,Code:integer;
begin
Track:=GetANSI(TrackString);
Index:=Pos('/',Track);
if Index=0 then
Val(Track,Value,Code)
else
Val(Copy(Track,1,Index-1),Value,Code);
if Code=0 then
Result:=Value
else
Result:=0;
end;

function ExtractYear(const YearString,DateString:string):string;
begin
Result:=GetANSI(YearString);
if Result='' then
Result:=Copy(GetANSI(DateString),1,4);
end;

function ExtractGenre(const GenreString:string):string;
begin
Result:=GetANSI(GenreString);
if Pos(')',Result)>0 then
Delete(Result,1,LastDelimiter(')',Result));
end;

function ExtractText(const SourceString:string;LanguageID:boolean):string;
var
Source,Separator:string;
EncodingID:char;
begin
Source:=SourceString;
Result:='';
if Length(Source)>0 then
begin
EncodingID:=Source[1];
if EncodingID=UNICODE_ID then
Separator:=#0#0
else
Separator:=#0;
if LanguageID then
Delete(Source,1,4)
else
Delete(Source, 1, 1);
Delete(Source,1,Pos(Separator,Source)+Length(Separator)-1);
Result:=GetANSI(EncodingID+Source);
end;
end;

procedure BuildHeader(var Tag:TagInfo);
var
Iterator,TagSize:integer;
begin
TagSize:=10;
for Iterator:=1 to ID3V2_FRAME_COUNT do
if Tag.Frame[Iterator]<>'' then
Inc(TagSize,Length(Tag.Frame[Iterator])+11);
Tag.NeedRewrite:=(Tag.ID<>ID3V2_ID)or(GetTagSize(Tag)<TagSize)or
(GetTagSize(Tag)>ID3V2_MAX_SIZE);
if Tag.NeedRewrite then
Tag.PaddingSize:=ID3V2_MAX_SIZE-TagSize
else
Tag.PaddingSize:=GetTagSize(Tag)-TagSize;
if Tag.PaddingSize>0 then
Inc(TagSize,Tag.PaddingSize);
Tag.ID:= ID3V2_ID;
Tag.Version:=TAG_VERSION_2_3;
Tag.Revision:=0;
Tag.Flags:= 0;
for Iterator:=1 to 4 do
Tag.Size[Iterator]:=((TagSize-10)shr((4-Iterator)*7)) and $7F;
end;

function ReplaceTag(const FileName:WideString;TagData:TStream):boolean;
var
Destination:TFileStream;
begin
Result:=False;
if (not FileExists(FileName))or(FileSetAttr(FileName,0)<>0) then
exit;
try
TagData.Position:=0;
Destination:= TFileStream.Create(FileName,fmOpenReadWrite);
Destination.CopyFrom(TagData,TagData.Size);
Destination.Free;
Result:=True;
except

end;
end;

function RebuildFile(const FileName:WideString;TagData:TStream):boolean;
var
Tag:TagInfo;
Source,Destination:TFileStream;
BufferName:string;
begin
Result:=False;
if (not FileExists(FileName))or(FileSetAttr(FileName,0)<>0) then
exit;
if not ReadHeader(FileName,Tag) then
exit;
if (TagData=nil) and (Tag.ID<>ID3V2_ID) then
exit;
try
BufferName:=FileName+'~';
Source:=TFileStream.Create(FileName,fmOpenRead);
Destination:=TFileStream.Create(BufferName,fmCreate);
if Tag.ID=ID3V2_ID then
Source.Seek(GetTagSize(Tag),soFromBeginning);
if TagData<>nil then
Destination.CopyFrom(TagData,0);
Destination.CopyFrom(Source, Source.Size-Source.Position);
Source.Free;
Destination.Free;
if (DeleteFile(FileName)) and (RenameFile(BufferName, FileName)) then
Result := True
else
raise Exception.Create('');
except
if FileExists(BufferName) then
DeleteFile(BufferName);
end;
end;

function SaveTag(const FileName:WideString;Tag:TagInfo):boolean;
var
TagData:TStringStream;
Iterator,FrameSize:integer;
Padding:array [1..ID3V2_MAX_SIZE] of byte;
begin
TagData:=TStringStream.Create('');
BuildHeader(Tag);
TagData.Write(Tag,10);
for Iterator := 1 to ID3V2_FRAME_COUNT do
if Tag.Frame[Iterator]<>'' then
begin
TagData.WriteString(ID3V2_FRAME_NEW[Iterator]);
FrameSize := Swap32(Length(Tag.Frame[Iterator])+1);
TagData.Write(FrameSize,SizeOf(FrameSize));
TagData.WriteString(#0#0#0+Tag.Frame[Iterator]);
end;
FillChar(Padding,SizeOf(Padding), 0);
if Tag.PaddingSize>0 then
TagData.Write(Padding,Tag.PaddingSize);
if Tag.NeedRewrite then
Result:=RebuildFile(FileName,TagData)
else
Result := ReplaceTag(FileName,TagData);
TagData.Free;
end;

procedure TID3v2.FSetTitle(const NewTitle:string);
begin
FTitle:=Trim(NewTitle);
end;

procedure TID3v2.FSetArtist(const NewArtist:string);
begin
FArtist:=Trim(NewArtist);
end;

procedure TID3v2.FSetAlbum(const NewAlbum:string);
begin
FAlbum:=Trim(NewAlbum);
end;

procedure TID3v2.FSetTrack(const NewTrack:word);
begin
FTrack:=NewTrack;
end;

procedure TID3v2.FSetYear(const NewYear:string);
begin
FYear:=Trim(NewYear);
end;

procedure TID3v2.FSetGenre(const NewGenre:string);
begin
FGenre:=Trim(NewGenre);
end;

procedure TID3v2.FSetComment(const NewComment:string);
begin
FComment:=Trim(NewComment);
end;

procedure TID3v2.FSetComposer(const NewComposer:string);
begin
FComposer:=Trim(NewComposer);
end;

procedure TID3v2.FSetEncoder(const NewEncoder:string);
begin
FEncoder:=Trim(NewEncoder);
end;

procedure TID3v2.FSetCopyright(const NewCopyright:string);
begin
FCopyright:=Trim(NewCopyright);
end;

procedure TID3v2.FSetLanguage(const NewLanguage:string);
begin
FLanguage:=Trim(NewLanguage);
end;

procedure TID3v2.FSetLink(const NewLink:string);
begin
FLink:=Trim(NewLink);
end;
constructor TID3v2.Create;
begin
inherited;
ResetData;
end;
procedure TID3v2.ResetData;
begin
FExists:=False;
FVersionID:=0;
FSize:=0;
FTitle:='';
FArtist:='';
FAlbum:='';
FTrack:=0;
FTrackString:='';
FYear:='';
FGenre:='';
FComment:='';
FComposer:='';
FEncoder:='';
FCopyright:='';
FLanguage:='';
FLink:='';
FTSIZ:='';
end;

function TID3v2.ReadFromFile(const FileName:WideString):boolean;
var
Tag:TagInfo;
begin
ResetData;
Result:=ReadHeader(FileName,Tag);
if (Result) and (Tag.ID=ID3V2_ID) then
begin
FExists:=True;
FVersionID:=Tag.Version;
FSize:=GetTagSize(Tag);
if (FVersionID in [TAG_VERSION_2_2..TAG_VERSION_2_4]) and (FSize>0) then
begin
if FVersionID>TAG_VERSION_2_2 then
ReadFramesNew(FileName,Tag)
else
ReadFramesOld(FileName,Tag);
FTitle:=GetContent(Tag.Frame[1],Tag.Frame[15]);
FArtist:=GetContent(Tag.Frame[2],Tag.Frame[14]);
FAlbum:=GetContent(Tag.Frame[3],Tag.Frame[16]);
FTrack:=ExtractTrack(Tag.Frame[4]);
FTrackString:=GetANSI(Tag.Frame[4]);
FYear:=ExtractYear(Tag.Frame[5],Tag.Frame[13]);
FGenre:=ExtractGenre(Tag.Frame[6]);
FComment:=ExtractText(Tag.Frame[7],True);
FComposer:=GetANSI(Tag.Frame[8]);
FEncoder:=GetANSI(Tag.Frame[9]);
FCopyright:=GetANSI(Tag.Frame[10]);
FLanguage:=GetANSI(Tag.Frame[11]);
FLink:=ExtractText(Tag.Frame[12], False);
FTSIZ:=GetANSI(Tag.Frame[17]);
end;
end;
end;

function TID3v2.SaveToFile(const FileName:WideString):boolean;
var
Tag:TagInfo;
begin
FillChar(Tag,SizeOf(Tag),0);
ReadHeader(FileName,Tag);
Tag.Frame[1]:=FTitle;
Tag.Frame[2]:=FArtist;
Tag.Frame[3]:=FAlbum;
if FTrack>0 then
Tag.Frame[4]:=IntToStr(FTrack);
Tag.Frame[5]:=FYear;
Tag.Frame[6]:=FGenre;
if FComment<>'' then
Tag.Frame[7]:='eng'+#0+FComment;
Tag.Frame[8]:=FComposer;
Tag.Frame[9]:=FEncoder;
Tag.Frame[10]:=FCopyright;
Tag.Frame[11]:=FLanguage;
if FLink<>'' then
Tag.Frame[12]:=#0+FLink;
Result:=SaveTag(FileName,Tag);
end;

function TID3v2.RemoveFromFile(const FileName:WideString):boolean;
begin
Result:=RebuildFile(FileName,nil);
end;

end.
Неизвестный
28.07.2008, 22:52
общий
Мне кажется что лучше изучать структуру тегов ID3v2 с помощью оф. документации. Её можно найти на сайте www.id3.org. Правда вся документация на английском языке.
Здесь Вы можете найти немного дополнительной информации.
Удачи!!!
давно
Мастер-Эксперт
425
4118
29.07.2008, 05:54
общий
Айтуган Сарбасов
Оё-ёй, как много букв.
Лучше дайте в мини-форуме ссылку на компонент, а то Ваше сообщение (да и весь мини-форум из-за него) читать неудобно.
Я его удаляю...
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Форма ответа