initial checkin

This commit is contained in:
chudov
2008-10-13 19:25:11 +00:00
parent 2e379c72e2
commit 36757fca7a
937 changed files with 184964 additions and 0 deletions

View File

@@ -0,0 +1,488 @@
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TAPEtag - for manipulating with APE tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.0 (21 April 2002) }
{ - Reading & writing support for APE 1.0 tags }
{ - Reading support for APE 2.0 tags (UTF-8 decoding) }
{ - Tag info: title, artist, album, track, year, genre, comment, copyright }
{ }
{ *************************************************************************** }
unit APEtag;
interface
uses
Classes, SysUtils;
type
{ Class TAPEtag }
TAPEtag = class(TObject)
private
{ Private declarations }
FExists: Boolean;
FVersion: Integer;
FSize: Integer;
FTitle: string;
FArtist: string;
FAlbum: string;
FTrack: Byte;
FYear: string;
FGenre: string;
FComment: string;
FCopyright: string;
procedure FSetTitle(const NewTitle: string);
procedure FSetArtist(const NewArtist: string);
procedure FSetAlbum(const NewAlbum: string);
procedure FSetTrack(const NewTrack: Byte);
procedure FSetYear(const NewYear: string);
procedure FSetGenre(const NewGenre: string);
procedure FSetComment(const NewComment: string);
procedure FSetCopyright(const NewCopyright: string);
public
{ Public declarations }
constructor Create; { Create object }
procedure ResetData; { Reset all data }
function ReadFromFile(const FileName: string): Boolean; { Load tag }
function RemoveFromFile(const FileName: string): Boolean; { Delete tag }
function SaveToFile(const FileName: string): Boolean; { Save tag }
property Exists: Boolean read FExists; { True if tag found }
property Version: Integer read FVersion; { Tag version }
property Size: Integer read FSize; { Total tag size }
property Title: string read FTitle write FSetTitle; { Song title }
property Artist: string read FArtist write FSetArtist; { Artist name }
property Album: string read FAlbum write FSetAlbum; { Album title }
property Track: Byte read FTrack write FSetTrack; { Track number }
property Year: string read FYear write FSetYear; { Release year }
property Genre: string read FGenre write FSetGenre; { Genre name }
property Comment: string read FComment write FSetComment; { Comment }
property Copyright: string read FCopyright write FSetCopyright; { (c) }
end;
implementation
const
{ Tag ID }
ID3V1_ID = 'TAG'; { ID3v1 }
APE_ID = 'APETAGEX'; { APE }
{ Size constants }
ID3V1_TAG_SIZE = 128; { ID3v1 tag }
APE_TAG_FOOTER_SIZE = 32; { APE tag footer }
APE_TAG_HEADER_SIZE = 32; { APE tag header }
{ First version of APE tag }
APE_VERSION_1_0 = 1000;
{ Max. number of supported tag fields }
APE_FIELD_COUNT = 8;
{ Names of supported tag fields }
APE_FIELD: array [1..APE_FIELD_COUNT] of string =
('Title', 'Artist', 'Album', 'Track', 'Year', 'Genre',
'Comment', 'Copyright');
type
{ APE tag data - for internal use }
TagInfo = record
{ Real structure of APE footer }
ID: array [1..8] of Char; { Always "APETAGEX" }
Version: Integer; { Tag version }
Size: Integer; { Tag size including footer }
Fields: Integer; { Number of fields }
Flags: Integer; { Tag flags }
Reserved: array [1..8] of Char; { Reserved for later use }
{ Extended data }
DataShift: Byte; { Used if ID3v1 tag found }
FileSize: Integer; { File size (bytes) }
Field: array [1..APE_FIELD_COUNT] of string; { Information from fields }
end;
{ ********************* Auxiliary functions & procedures ******************** }
function ReadFooter(const FileName: string; var Tag: TagInfo): Boolean;
var
SourceFile: file;
TagID: array [1..3] of Char;
Transferred: Integer;
begin
{ Load footer from file to variable }
try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
Tag.FileSize := FileSize(SourceFile);
{ Check for existing ID3v1 tag }
Seek(SourceFile, Tag.FileSize - ID3V1_TAG_SIZE);
BlockRead(SourceFile, TagID, SizeOf(TagID));
if TagID = ID3V1_ID then Tag.DataShift := ID3V1_TAG_SIZE;
{ Read footer data }
Seek(SourceFile, Tag.FileSize - Tag.DataShift - APE_TAG_FOOTER_SIZE);
BlockRead(SourceFile, Tag, APE_TAG_FOOTER_SIZE, Transferred);
CloseFile(SourceFile);
{ if transfer is not complete }
if Transferred < APE_TAG_FOOTER_SIZE then Result := false;
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function ConvertFromUTF8(const Source: string): string;
var
Iterator, SourceLength, FChar, NChar: Integer;
begin
{ Convert UTF-8 string to ANSI string }
Result := '';
Iterator := 0;
SourceLength := Length(Source);
while Iterator < SourceLength do
begin
Inc(Iterator);
FChar := Ord(Source[Iterator]);
if FChar >= $80 then
begin
Inc(Iterator);
if Iterator > SourceLength then break;
FChar := FChar and $3F;
if (FChar and $20) <> 0 then
begin
FChar := FChar and $1F;
NChar := Ord(Source[Iterator]);
if (NChar and $C0) <> $80 then break;
FChar := (FChar shl 6) or (NChar and $3F);
Inc(Iterator);
if Iterator > SourceLength then break;
end;
NChar := Ord(Source[Iterator]);
if (NChar and $C0) <> $80 then break;
Result := Result + WideChar((FChar shl 6) or (NChar and $3F));
end
else
Result := Result + WideChar(FChar);
end;
end;
{ --------------------------------------------------------------------------- }
procedure SetTagItem(const FieldName, FieldValue: string; var Tag: TagInfo);
var
Iterator: Byte;
begin
{ Set tag item if supported field found }
for Iterator := 1 to APE_FIELD_COUNT do
if UpperCase(FieldName) = UpperCase(APE_FIELD[Iterator]) then
if Tag.Version > APE_VERSION_1_0 then
Tag.Field[Iterator] := ConvertFromUTF8(FieldValue)
else
Tag.Field[Iterator] := FieldValue;
end;
{ --------------------------------------------------------------------------- }
procedure ReadFields(const FileName: string; var Tag: TagInfo);
var
SourceFile: file;
FieldName: string;
FieldValue: array [1..250] of Char;
NextChar: Char;
Iterator, ValueSize, ValuePosition, FieldFlags: Integer;
begin
try
{ Set read-access, open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
Seek(SourceFile, Tag.FileSize - Tag.DataShift - Tag.Size);
{ Read all stored fields }
for Iterator := 1 to Tag.Fields do
begin
FillChar(FieldValue, SizeOf(FieldValue), 0);
BlockRead(SourceFile, ValueSize, SizeOf(ValueSize));
BlockRead(SourceFile, FieldFlags, SizeOf(FieldFlags));
FieldName := '';
repeat
BlockRead(SourceFile, NextChar, SizeOf(NextChar));
FieldName := FieldName + NextChar;
until Ord(NextChar) = 0;
ValuePosition := FilePos(SourceFile);
BlockRead(SourceFile, FieldValue, ValueSize mod SizeOf(FieldValue));
SetTagItem(Trim(FieldName), Trim(FieldValue), Tag);
Seek(SourceFile, ValuePosition + ValueSize);
end;
CloseFile(SourceFile);
except
end;
end;
{ --------------------------------------------------------------------------- }
function GetTrack(const TrackString: string): Byte;
var
Index, Value, Code: Integer;
begin
{ Get track from string }
Index := Pos('/', TrackString);
if Index = 0 then Val(TrackString, Value, Code)
else Val(Copy(TrackString, 1, Index - 1), Value, Code);
if Code = 0 then Result := Value
else Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TruncateFile(const FileName: string; TagSize: Integer): Boolean;
var
SourceFile: file;
begin
try
Result := true;
{ Allow write-access and open file }
FileSetAttr(FileName, 0);
AssignFile(SourceFile, FileName);
FileMode := 2;
Reset(SourceFile, 1);
{ Delete tag }
Seek(SourceFile, FileSize(SourceFile) - TagSize);
Truncate(SourceFile);
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
procedure BuildFooter(var Tag: TagInfo);
var
Iterator: Integer;
begin
{ Build tag footer }
Tag.ID := APE_ID;
Tag.Version := APE_VERSION_1_0;
Tag.Size := APE_TAG_FOOTER_SIZE;
for Iterator := 1 to APE_FIELD_COUNT do
if Tag.Field[Iterator] <> '' then
begin
Inc(Tag.Size, Length(APE_FIELD[Iterator] + Tag.Field[Iterator]) + 10);
Inc(Tag.Fields);
end;
end;
{ --------------------------------------------------------------------------- }
function AddToFile(const FileName: string; TagData: TStream): Boolean;
var
FileData: TFileStream;
begin
try
{ Add tag data to file }
FileData := TFileStream.Create(FileName, fmOpenWrite or fmShareExclusive);
FileData.Seek(0, soFromEnd);
TagData.Seek(0, soFromBeginning);
FileData.CopyFrom(TagData, TagData.Size);
FileData.Free;
Result := true;
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function SaveTag(const FileName: string; Tag: TagInfo): Boolean;
var
TagData: TStringStream;
Iterator, ValueSize, Flags: Integer;
begin
{ Build and write tag fields and footer to stream }
TagData := TStringStream.Create('');
for Iterator := 1 to APE_FIELD_COUNT do
if Tag.Field[Iterator] <> '' then
begin
ValueSize := Length(Tag.Field[Iterator]) + 1;
Flags := 0;
TagData.Write(ValueSize, SizeOf(ValueSize));
TagData.Write(Flags, SizeOf(Flags));
TagData.WriteString(APE_FIELD[Iterator] + #0);
TagData.WriteString(Tag.Field[Iterator] + #0);
end;
BuildFooter(Tag);
TagData.Write(Tag, APE_TAG_FOOTER_SIZE);
{ Add created tag to file }
Result := AddToFile(FileName, TagData);
TagData.Free;
end;
{ ********************** Private functions & procedures ********************* }
procedure TAPEtag.FSetTitle(const NewTitle: string);
begin
{ Set song title }
FTitle := Trim(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetArtist(const NewArtist: string);
begin
{ Set artist name }
FArtist := Trim(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetAlbum(const NewAlbum: string);
begin
{ Set album title }
FAlbum := Trim(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetTrack(const NewTrack: Byte);
begin
{ Set track number }
FTrack := NewTrack;
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetYear(const NewYear: string);
begin
{ Set release year }
FYear := Trim(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetGenre(const NewGenre: string);
begin
{ Set genre name }
FGenre := Trim(NewGenre);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetComment(const NewComment: string);
begin
{ Set comment }
FComment := Trim(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.FSetCopyright(const NewCopyright: string);
begin
{ Set copyright information }
FCopyright := Trim(NewCopyright);
end;
{ ********************** Public functions & procedures ********************** }
constructor TAPEtag.Create;
begin
{ Create object }
inherited;
ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TAPEtag.ResetData;
begin
{ Reset all variables }
FExists := false;
FVersion := 0;
FSize := 0;
FTitle := '';
FArtist := '';
FAlbum := '';
FTrack := 0;
FYear := '';
FGenre := '';
FComment := '';
FCopyright := '';
end;
{ --------------------------------------------------------------------------- }
function TAPEtag.ReadFromFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Reset data and load footer from file to variable }
ResetData;
FillChar(Tag, SizeOf(Tag), 0);
Result := ReadFooter(FileName, Tag);
{ Process data if loaded and footer valid }
if (Result) and (Tag.ID = APE_ID) then
begin
FExists := true;
{ Fill properties with footer data }
FVersion := Tag.Version;
FSize := Tag.Size;
{ Get information from fields }
ReadFields(FileName, Tag);
FTitle := Tag.Field[1];
FArtist := Tag.Field[2];
FAlbum := Tag.Field[3];
FTrack := GetTrack(Tag.Field[4]);
FYear := Tag.Field[5];
FGenre := Tag.Field[6];
FComment := Tag.Field[7];
FCopyright := Tag.Field[8];
end;
end;
{ --------------------------------------------------------------------------- }
function TAPEtag.RemoveFromFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Remove tag from file if found }
FillChar(Tag, SizeOf(Tag), 0);
if ReadFooter(FileName, Tag) then
begin
if Tag.ID <> APE_ID then Tag.Size := 0;
if (Tag.Flags shr 31) > 0 then Inc(Tag.Size, APE_TAG_HEADER_SIZE);
Result := TruncateFile(FileName, Tag.DataShift + Tag.Size)
end
else
Result := false;
end;
{ --------------------------------------------------------------------------- }
function TAPEtag.SaveToFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Prepare tag data and save to file }
FillChar(Tag, SizeOf(Tag), 0);
Tag.Field[1] := FTitle;
Tag.Field[2] := FArtist;
Tag.Field[3] := FAlbum;
if FTrack > 0 then Tag.Field[4] := IntToStr(FTrack);
Tag.Field[5] := FYear;
Tag.Field[6] := FGenre;
Tag.Field[7] := FComment;
Tag.Field[8] := FCopyright;
{ Delete old tag if exists and write new tag }
Result := (RemoveFromFile(FileName)) and (SaveTag(FileName, Tag));
end;
end.

View File

@@ -0,0 +1,18 @@
APEtag.pas
Tested with Borland Delphi 3,4,5,6
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TAPEtag - for manipulating with APE tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.0 (21 April 2002) }
{ - Reading & writing support for APE 1.0 tags }
{ - Reading support for APE 2.0 tags (UTF-8 decoding) }
{ - Tag info: title, artist, album, track, year, genre, comment, copyright }
{ }
{ *************************************************************************** }

Binary file not shown.

View File

@@ -0,0 +1,159 @@
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl, ExtCtrls, APEtag;
type
TMainForm = class(TForm)
DriveList: TDriveComboBox;
FolderList: TDirectoryListBox;
FileList: TFileListBox;
SaveButton: TButton;
RemoveButton: TButton;
CloseButton: TButton;
InfoBevel: TBevel;
IconImage: TImage;
TagExistsLabel: TLabel;
TagExistsValue: TEdit;
VersionLabel: TLabel;
VersionValue: TEdit;
SizeLabel: TLabel;
SizeValue: TEdit;
TitleLabel: TLabel;
TitleEdit: TEdit;
ArtistLabel: TLabel;
ArtistEdit: TEdit;
AlbumLabel: TLabel;
AlbumEdit: TEdit;
TrackLabel: TLabel;
TrackEdit: TEdit;
YearLabel: TLabel;
YearEdit: TEdit;
GenreLabel: TLabel;
GenreEdit: TEdit;
CommentLabel: TLabel;
CommentEdit: TEdit;
CopyrightLabel: TLabel;
CopyrightEdit: TEdit;
procedure FormCreate(Sender: TObject);
procedure FileListChange(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SaveButtonClick(Sender: TObject);
procedure RemoveButtonClick(Sender: TObject);
procedure CloseButtonClick(Sender: TObject);
private
{ Private declarations }
FileTag: TAPEtag;
procedure ClearAll;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.ClearAll;
begin
{ Clear all captions }
TagExistsValue.Text := '';
VersionValue.Text := '';
SizeValue.Text := '';
TitleEdit.Text := '';
ArtistEdit.Text := '';
AlbumEdit.Text := '';
TrackEdit.Text := '';
YearEdit.Text := '';
GenreEdit.Text := '';
CommentEdit.Text := '';
CopyrightEdit.Text := '';
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Create object and clear captions }
FileTag := TAPEtag.Create;
ClearAll;
end;
procedure TMainForm.FileListChange(Sender: TObject);
begin
{ Clear captions }
ClearAll;
if FileList.FileName = '' then exit;
if FileExists(FileList.FileName) then
{ Load tag data }
if FileTag.ReadFromFile(FileList.FileName) then
if FileTag.Exists then
begin
{ Fill captions }
TagExistsValue.Text := 'Yes';
VersionValue.Text := FormatFloat('0,000', FileTag.Version);
SizeValue.Text := IntToStr(FileTag.Size) + ' bytes';
TitleEdit.Text := FileTag.Title;
ArtistEdit.Text := FileTag.Artist;
AlbumEdit.Text := FileTag.Album;
if FileTag.Track > 0 then TrackEdit.Text := IntToStr(FileTag.Track);
YearEdit.Text := FileTag.Year;
GenreEdit.Text := FileTag.Genre;
CommentEdit.Text := FileTag.Comment;
CopyrightEdit.Text := FileTag.Copyright;
end
else
{ Tag not found }
TagExistsValue.Text := 'No'
else
{ Read error }
ShowMessage('Can not read tag from the file: ' + FileList.FileName)
else
{ File does not exist }
ShowMessage('The file does not exist: ' + FileList.FileName);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ Free memory }
FileTag.Free;
end;
procedure TMainForm.SaveButtonClick(Sender: TObject);
var
Value, Code: Integer;
begin
{ Prepare tag data }
FileTag.Title := TitleEdit.Text;
FileTag.Artist := ArtistEdit.Text;
FileTag.Album := AlbumEdit.Text;
Val(TrackEdit.Text, Value, Code);
if (Code = 0) and (Value > 0) then FileTag.Track := Value
else FileTag.Track := 0;
FileTag.Year := YearEdit.Text;
FileTag.Genre := GenreEdit.Text;
FileTag.Comment := CommentEdit.Text;
FileTag.Copyright := CopyrightEdit.Text;
{ Save tag data }
if (not FileExists(FileList.FileName)) or
(not FileTag.SaveToFile(FileList.FileName)) then
ShowMessage('Can not save tag to the file: ' + FileList.FileName);
FileListChange(Self);
end;
procedure TMainForm.RemoveButtonClick(Sender: TObject);
begin
{ Delete tag data }
if (FileExists(FileList.FileName)) and
(FileTag.RemoveFromFile(FileList.FileName)) then ClearAll
else ShowMessage('Can not remove tag from the file: ' + FileList.FileName);
end;
procedure TMainForm.CloseButtonClick(Sender: TObject);
begin
{ Exit }
Close;
end;
end.

View File

@@ -0,0 +1,35 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-LE"c:\program files\borland\delphi6\Projects\Bpl"
-LN"c:\program files\borland\delphi6\Projects\Bpl"

View File

@@ -0,0 +1,96 @@
[FileVersion]
Version=6.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;dbexpress;dbxcds;indy;dclOffice2k
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Language]
ActiveLang=
ProjectLang=
RootDir=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1031
CodePage=1252
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlUnitOutputDirectory]
Count=1
Item0=DCU
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=Data

View File

@@ -0,0 +1,15 @@
program Test;
uses
Forms,
Main in 'Main.pas' {MainForm},
APEtag in 'APEtag.pas';
{$R *.res}
begin
Application.Initialize;
Application.Title := 'APEtag Test';
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

Binary file not shown.

View File

@@ -0,0 +1,475 @@
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TID3v1 - for manipulating with ID3v1 tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.0 (25 July 2001) }
{ - Reading & writing support for ID3v1.x tags }
{ - Tag info: title, artist, album, track, year, genre, comment }
{ }
{ *************************************************************************** }
unit ID3v1;
interface
uses
Classes, SysUtils;
const
MAX_MUSIC_GENRES = 148; { Max. number of music genres }
DEFAULT_GENRE = 255; { Index for default genre }
{ Used with VersionID property }
TAG_VERSION_1_0 = 1; { Index for ID3v1.0 tag }
TAG_VERSION_1_1 = 2; { Index for ID3v1.1 tag }
var
MusicGenre: array [0..MAX_MUSIC_GENRES - 1] of string; { Genre names }
type
{ Used in TID3v1 class }
String04 = string[4]; { String with max. 4 symbols }
String30 = string[30]; { String with max. 30 symbols }
{ Class TID3v1 }
TID3v1 = class(TObject)
private
{ Private declarations }
FExists: Boolean;
FVersionID: Byte;
FTitle: String30;
FArtist: String30;
FAlbum: String30;
FYear: String04;
FComment: String30;
FTrack: Byte;
FGenreID: Byte;
procedure FSetTitle(const NewTitle: String30);
procedure FSetArtist(const NewArtist: String30);
procedure FSetAlbum(const NewAlbum: String30);
procedure FSetYear(const NewYear: String04);
procedure FSetComment(const NewComment: String30);
procedure FSetTrack(const NewTrack: Byte);
procedure FSetGenreID(const NewGenreID: Byte);
function FGetGenre: string;
public
{ Public declarations }
constructor Create; { Create object }
procedure ResetData; { Reset all data }
function ReadFromFile(const FileName: string): Boolean; { Load tag }
function RemoveFromFile(const FileName: string): Boolean; { Delete tag }
function SaveToFile(const FileName: string): Boolean; { Save tag }
property Exists: Boolean read FExists; { True if tag found }
property VersionID: Byte read FVersionID; { Version code }
property Title: String30 read FTitle write FSetTitle; { Song title }
property Artist: String30 read FArtist write FSetArtist; { Artist name }
property Album: String30 read FAlbum write FSetAlbum; { Album name }
property Year: String04 read FYear write FSetYear; { Year }
property Comment: String30 read FComment write FSetComment; { Comment }
property Track: Byte read FTrack write FSetTrack; { Track number }
property GenreID: Byte read FGenreID write FSetGenreID; { Genre code }
property Genre: string read FGetGenre; { Genre name }
end;
implementation
type
{ Real structure of ID3v1 tag }
TagRecord = record
Header: array [1..3] of Char; { Tag header - must be "TAG" }
Title: array [1..30] of Char; { Title data }
Artist: array [1..30] of Char; { Artist data }
Album: array [1..30] of Char; { Album data }
Year: array [1..4] of Char; { Year data }
Comment: array [1..30] of Char; { Comment data }
Genre: Byte; { Genre data }
end;
{ ********************* Auxiliary functions & procedures ******************** }
function ReadTag(const FileName: string; var TagData: TagRecord): Boolean;
var
SourceFile: file;
begin
try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
{ Read tag }
Seek(SourceFile, FileSize(SourceFile) - 128);
BlockRead(SourceFile, TagData, 128);
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function RemoveTag(const FileName: string): Boolean;
var
SourceFile: file;
begin
try
Result := true;
{ Allow write-access and open file }
FileSetAttr(FileName, 0);
AssignFile(SourceFile, FileName);
FileMode := 2;
Reset(SourceFile, 1);
{ Delete tag }
Seek(SourceFile, FileSize(SourceFile) - 128);
Truncate(SourceFile);
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function SaveTag(const FileName: string; TagData: TagRecord): Boolean;
var
SourceFile: file;
begin
try
Result := true;
{ Allow write-access and open file }
FileSetAttr(FileName, 0);
AssignFile(SourceFile, FileName);
FileMode := 2;
Reset(SourceFile, 1);
{ Write tag }
Seek(SourceFile, FileSize(SourceFile));
BlockWrite(SourceFile, TagData, SizeOf(TagData));
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function GetTagVersion(const TagData: TagRecord): Byte;
begin
Result := TAG_VERSION_1_0;
{ Terms for ID3v1.1 }
if ((TagData.Comment[29] = #0) and (TagData.Comment[30] <> #0)) or
((TagData.Comment[29] = #32) and (TagData.Comment[30] <> #32)) then
Result := TAG_VERSION_1_1;
end;
{ ********************** Private functions & procedures ********************* }
procedure TID3v1.FSetTitle(const NewTitle: String30);
begin
FTitle := TrimRight(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetArtist(const NewArtist: String30);
begin
FArtist := TrimRight(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetAlbum(const NewAlbum: String30);
begin
FAlbum := TrimRight(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetYear(const NewYear: String04);
begin
FYear := TrimRight(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetComment(const NewComment: String30);
begin
FComment := TrimRight(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetTrack(const NewTrack: Byte);
begin
FTrack := NewTrack;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetGenreID(const NewGenreID: Byte);
begin
FGenreID := NewGenreID;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.FGetGenre: string;
begin
Result := '';
{ Return an empty string if the current GenreID is not valid }
if FGenreID in [0..MAX_MUSIC_GENRES - 1] then Result := MusicGenre[FGenreID];
end;
{ ********************** Public functions & procedures ********************** }
constructor TID3v1.Create;
begin
inherited;
ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.ResetData;
begin
FExists := false;
FVersionID := TAG_VERSION_1_0;
FTitle := '';
FArtist := '';
FAlbum := '';
FYear := '';
FComment := '';
FTrack := 0;
FGenreID := DEFAULT_GENRE;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.ReadFromFile(const FileName: string): Boolean;
var
TagData: TagRecord;
begin
{ Reset and load tag data from file to variable }
ResetData;
Result := ReadTag(FileName, TagData);
{ Process data if loaded and tag header OK }
if (Result) and (TagData.Header = 'TAG') then
begin
FExists := true;
FVersionID := GetTagVersion(TagData);
{ Fill properties with tag data }
FTitle := TrimRight(TagData.Title);
FArtist := TrimRight(TagData.Artist);
FAlbum := TrimRight(TagData.Album);
FYear := TrimRight(TagData.Year);
if FVersionID = TAG_VERSION_1_0 then
FComment := TrimRight(TagData.Comment)
else
begin
FComment := TrimRight(Copy(TagData.Comment, 1, 28));
FTrack := Ord(TagData.Comment[30]);
end;
FGenreID := TagData.Genre;
end;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.RemoveFromFile(const FileName: string): Boolean;
var
TagData: TagRecord;
begin
{ Find tag }
Result := ReadTag(FileName, TagData);
{ Delete tag if loaded and tag header OK }
if (Result) and (TagData.Header = 'TAG') then Result := RemoveTag(FileName);
end;
{ --------------------------------------------------------------------------- }
function TID3v1.SaveToFile(const FileName: string): Boolean;
var
TagData: TagRecord;
begin
{ Prepare tag record }
FillChar(TagData, SizeOf(TagData), 0);
TagData.Header := 'TAG';
Move(FTitle[1], TagData.Title, Length(FTitle));
Move(FArtist[1], TagData.Artist, Length(FArtist));
Move(FAlbum[1], TagData.Album, Length(FAlbum));
Move(FYear[1], TagData.Year, Length(FYear));
Move(FComment[1], TagData.Comment, Length(FComment));
if FTrack > 0 then
begin
TagData.Comment[29] := #0;
TagData.Comment[30] := Chr(FTrack);
end;
TagData.Genre := FGenreID;
{ Delete old tag and write new tag }
Result := (RemoveFromFile(FileName)) and (SaveTag(FileName, TagData));
end;
{ ************************** Initialize music genres ************************ }
initialization
begin
{ Standard genres }
MusicGenre[0] := 'Blues';
MusicGenre[1] := 'Classic Rock';
MusicGenre[2] := 'Country';
MusicGenre[3] := 'Dance';
MusicGenre[4] := 'Disco';
MusicGenre[5] := 'Funk';
MusicGenre[6] := 'Grunge';
MusicGenre[7] := 'Hip-Hop';
MusicGenre[8] := 'Jazz';
MusicGenre[9] := 'Metal';
MusicGenre[10] := 'New Age';
MusicGenre[11] := 'Oldies';
MusicGenre[12] := 'Other';
MusicGenre[13] := 'Pop';
MusicGenre[14] := 'R&B';
MusicGenre[15] := 'Rap';
MusicGenre[16] := 'Reggae';
MusicGenre[17] := 'Rock';
MusicGenre[18] := 'Techno';
MusicGenre[19] := 'Industrial';
MusicGenre[20] := 'Alternative';
MusicGenre[21] := 'Ska';
MusicGenre[22] := 'Death Metal';
MusicGenre[23] := 'Pranks';
MusicGenre[24] := 'Soundtrack';
MusicGenre[25] := 'Euro-Techno';
MusicGenre[26] := 'Ambient';
MusicGenre[27] := 'Trip-Hop';
MusicGenre[28] := 'Vocal';
MusicGenre[29] := 'Jazz+Funk';
MusicGenre[30] := 'Fusion';
MusicGenre[31] := 'Trance';
MusicGenre[32] := 'Classical';
MusicGenre[33] := 'Instrumental';
MusicGenre[34] := 'Acid';
MusicGenre[35] := 'House';
MusicGenre[36] := 'Game';
MusicGenre[37] := 'Sound Clip';
MusicGenre[38] := 'Gospel';
MusicGenre[39] := 'Noise';
MusicGenre[40] := 'AlternRock';
MusicGenre[41] := 'Bass';
MusicGenre[42] := 'Soul';
MusicGenre[43] := 'Punk';
MusicGenre[44] := 'Space';
MusicGenre[45] := 'Meditative';
MusicGenre[46] := 'Instrumental Pop';
MusicGenre[47] := 'Instrumental Rock';
MusicGenre[48] := 'Ethnic';
MusicGenre[49] := 'Gothic';
MusicGenre[50] := 'Darkwave';
MusicGenre[51] := 'Techno-Industrial';
MusicGenre[52] := 'Electronic';
MusicGenre[53] := 'Pop-Folk';
MusicGenre[54] := 'Eurodance';
MusicGenre[55] := 'Dream';
MusicGenre[56] := 'Southern Rock';
MusicGenre[57] := 'Comedy';
MusicGenre[58] := 'Cult';
MusicGenre[59] := 'Gangsta';
MusicGenre[60] := 'Top 40';
MusicGenre[61] := 'Christian Rap';
MusicGenre[62] := 'Pop/Funk';
MusicGenre[63] := 'Jungle';
MusicGenre[64] := 'Native American';
MusicGenre[65] := 'Cabaret';
MusicGenre[66] := 'New Wave';
MusicGenre[67] := 'Psychadelic';
MusicGenre[68] := 'Rave';
MusicGenre[69] := 'Showtunes';
MusicGenre[70] := 'Trailer';
MusicGenre[71] := 'Lo-Fi';
MusicGenre[72] := 'Tribal';
MusicGenre[73] := 'Acid Punk';
MusicGenre[74] := 'Acid Jazz';
MusicGenre[75] := 'Polka';
MusicGenre[76] := 'Retro';
MusicGenre[77] := 'Musical';
MusicGenre[78] := 'Rock & Roll';
MusicGenre[79] := 'Hard Rock';
{ Extended genres }
MusicGenre[80] := 'Folk';
MusicGenre[81] := 'Folk-Rock';
MusicGenre[82] := 'National Folk';
MusicGenre[83] := 'Swing';
MusicGenre[84] := 'Fast Fusion';
MusicGenre[85] := 'Bebob';
MusicGenre[86] := 'Latin';
MusicGenre[87] := 'Revival';
MusicGenre[88] := 'Celtic';
MusicGenre[89] := 'Bluegrass';
MusicGenre[90] := 'Avantgarde';
MusicGenre[91] := 'Gothic Rock';
MusicGenre[92] := 'Progessive Rock';
MusicGenre[93] := 'Psychedelic Rock';
MusicGenre[94] := 'Symphonic Rock';
MusicGenre[95] := 'Slow Rock';
MusicGenre[96] := 'Big Band';
MusicGenre[97] := 'Chorus';
MusicGenre[98] := 'Easy Listening';
MusicGenre[99] := 'Acoustic';
MusicGenre[100]:= 'Humour';
MusicGenre[101]:= 'Speech';
MusicGenre[102]:= 'Chanson';
MusicGenre[103]:= 'Opera';
MusicGenre[104]:= 'Chamber Music';
MusicGenre[105]:= 'Sonata';
MusicGenre[106]:= 'Symphony';
MusicGenre[107]:= 'Booty Bass';
MusicGenre[108]:= 'Primus';
MusicGenre[109]:= 'Porn Groove';
MusicGenre[110]:= 'Satire';
MusicGenre[111]:= 'Slow Jam';
MusicGenre[112]:= 'Club';
MusicGenre[113]:= 'Tango';
MusicGenre[114]:= 'Samba';
MusicGenre[115]:= 'Folklore';
MusicGenre[116]:= 'Ballad';
MusicGenre[117]:= 'Power Ballad';
MusicGenre[118]:= 'Rhythmic Soul';
MusicGenre[119]:= 'Freestyle';
MusicGenre[120]:= 'Duet';
MusicGenre[121]:= 'Punk Rock';
MusicGenre[122]:= 'Drum Solo';
MusicGenre[123]:= 'A capella';
MusicGenre[124]:= 'Euro-House';
MusicGenre[125]:= 'Dance Hall';
MusicGenre[126]:= 'Goa';
MusicGenre[127]:= 'Drum & Bass';
MusicGenre[128]:= 'Club-House';
MusicGenre[129]:= 'Hardcore';
MusicGenre[130]:= 'Terror';
MusicGenre[131]:= 'Indie';
MusicGenre[132]:= 'BritPop';
MusicGenre[133]:= 'Negerpunk';
MusicGenre[134]:= 'Polsk Punk';
MusicGenre[135]:= 'Beat';
MusicGenre[136]:= 'Christian Gangsta Rap';
MusicGenre[137]:= 'Heavy Metal';
MusicGenre[138]:= 'Black Metal';
MusicGenre[139]:= 'Crossover';
MusicGenre[140]:= 'Contemporary Christian';
MusicGenre[141]:= 'Christian Rock';
MusicGenre[142]:= 'Merengue';
MusicGenre[143]:= 'Salsa';
MusicGenre[144]:= 'Trash Metal';
MusicGenre[145]:= 'Anime';
MusicGenre[146]:= 'JPop';
MusicGenre[147]:= 'Synthpop';
end;
end.

View File

@@ -0,0 +1,17 @@
ID3v1.pas
Tested with Borland Delphi 3,4,5,6
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TID3v1 - for manipulating with ID3v1 tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.0 (25 July 2001) }
{ - Reading & writing support for ID3v1.x tags }
{ - Tag info: title, artist, album, track, year, genre, comment }
{ }
{ *************************************************************************** }

Binary file not shown.

View File

@@ -0,0 +1,162 @@
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl, ExtCtrls, ID3v1;
type
TMainForm = class(TForm)
DriveList: TDriveComboBox;
FolderList: TDirectoryListBox;
FileList: TFileListBox;
CloseButton: TButton;
RemoveButton: TButton;
SaveButton: TButton;
InfoBevel: TBevel;
IconImage: TImage;
TagExistsLabel: TLabel;
TagVersionLabel: TLabel;
TitleLabel: TLabel;
ArtistLabel: TLabel;
AlbumLabel: TLabel;
YearLabel: TLabel;
CommentLabel: TLabel;
TrackLabel: TLabel;
GenreLabel: TLabel;
TitleEdit: TEdit;
ArtistEdit: TEdit;
AlbumEdit: TEdit;
TrackEdit: TEdit;
YearEdit: TEdit;
CommentEdit: TEdit;
GenreComboBox: TComboBox;
TagExistsValue: TEdit;
TagVersionValue: TEdit;
procedure CloseButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FileListChange(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SaveButtonClick(Sender: TObject);
procedure RemoveButtonClick(Sender: TObject);
private
{ Private declarations }
FileTag: TID3v1;
procedure ClearAll;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.ClearAll;
begin
{ Clear all captions }
TagExistsValue.Text := '';
TagVersionValue.Text := '';
TitleEdit.Text := '';
ArtistEdit.Text := '';
AlbumEdit.Text := '';
TrackEdit.Text := '';
YearEdit.Text := '';
GenreComboBox.ItemIndex := 0;
CommentEdit.Text := '';
end;
procedure TMainForm.CloseButtonClick(Sender: TObject);
begin
{ Exit }
Close;
end;
procedure TMainForm.FormCreate(Sender: TObject);
var
Iterator: Integer;
begin
{ Create object }
FileTag := TID3v1.Create;
{ Fill and initialize genres }
GenreComboBox.Items.Add('');
for Iterator := 0 to MAX_MUSIC_GENRES - 1 do
GenreComboBox.Items.Add(MusicGenre[Iterator]);
{ Reset }
ClearAll;
end;
procedure TMainForm.FileListChange(Sender: TObject);
begin
{ Clear captions }
ClearAll;
if FileList.FileName = '' then exit;
if FileExists(FileList.FileName) then
{ Load tag data }
if FileTag.ReadFromFile(FileList.FileName) then
if FileTag.Exists then
begin
{ Fill captions }
TagExistsValue.Text := 'Yes';
if FileTag.VersionID = TAG_VERSION_1_0 then
TagVersionValue.Text := '1.0'
else
TagVersionValue.Text := '1.1';
TitleEdit.Text := FileTag.Title;
ArtistEdit.Text := FileTag.Artist;
AlbumEdit.Text := FileTag.Album;
TrackEdit.Text := IntToStr(FileTag.Track);
YearEdit.Text := FileTag.Year;
if FileTag.GenreID < MAX_MUSIC_GENRES then
GenreComboBox.ItemIndex := FileTag.GenreID + 1;
CommentEdit.Text := FileTag.Comment;
end
else
{ Tag not found }
TagExistsValue.Text := 'No'
else
{ Read error }
ShowMessage('Can not read tag from the file: ' + FileList.FileName)
else
{ File does not exist }
ShowMessage('The file does not exist: ' + FileList.FileName);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ Free memory }
FileTag.Free;
end;
procedure TMainForm.SaveButtonClick(Sender: TObject);
var
Value, Code: Integer;
begin
{ Prepare tag data }
FileTag.Title := TitleEdit.Text;
FileTag.Artist := ArtistEdit.Text;
FileTag.Album := AlbumEdit.Text;
FileTag.Year := YearEdit.Text;
Val(TrackEdit.Text, Value, Code);
if (Code = 0) and (Value > 0) then FileTag.Track := Value
else FileTag.Track := 0;
if GenreComboBox.ItemIndex = 0 then FileTag.GenreID := DEFAULT_GENRE
else FileTag.GenreID := GenreComboBox.ItemIndex - 1;
FileTag.Comment := CommentEdit.Text;
{ Save tag data }
if (not FileExists(FileList.FileName)) or
(not FileTag.SaveToFile(FileList.FileName)) then
ShowMessage('Can not save tag to the file: ' + FileList.FileName);
FileListChange(Self);
end;
procedure TMainForm.RemoveButtonClick(Sender: TObject);
begin
{ Delete tag data }
if (FileExists(FileList.FileName)) and
(FileTag.RemoveFromFile(FileList.FileName)) then ClearAll
else ShowMessage('Can not remove tag from the file: ' + FileList.FileName);
end;
end.

View File

@@ -0,0 +1,35 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-LE"c:\program files\borland\delphi6\Projects\Bpl"
-LN"c:\program files\borland\delphi6\Projects\Bpl"

View File

@@ -0,0 +1,96 @@
[FileVersion]
Version=6.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;dbexpress;dbxcds;indy;dclOffice2k
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Language]
ActiveLang=
ProjectLang=
RootDir=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1031
CodePage=1252
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlUnitOutputDirectory]
Count=1
Item0=DCU
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=Data

View File

@@ -0,0 +1,15 @@
program Test;
uses
Forms,
Main in 'Main.pas' {MainForm},
ID3v1 in 'ID3v1.pas';
{$R *.res}
begin
Application.Initialize;
Application.Title := 'ID3v1 Test';
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

Binary file not shown.

View File

@@ -0,0 +1,631 @@
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TID3v2 - for manipulating with ID3v2 tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.4 (24 March 2002) }
{ - Reading support for ID3v2.2.x & ID3v2.4.x tags }
{ }
{ Version 1.3 (16 February 2002) }
{ - Fixed bug with property Comment }
{ - Added info: composer, encoder, copyright, language, link }
{ }
{ Version 1.2 (17 October 2001) }
{ - Writing support for ID3v2.3.x tags }
{ - Fixed bug with track number detection }
{ - Fixed bug with tag reading }
{ }
{ Version 1.1 (31 August 2001) }
{ - Added public procedure ResetData }
{ }
{ Version 1.0 (14 August 2001) }
{ - Reading support for ID3v2.3.x tags }
{ - Tag info: title, artist, album, track, year, genre, comment }
{ }
{ *************************************************************************** }
unit ID3v2;
interface
uses
Classes, SysUtils;
const
TAG_VERSION_2_2 = 2; { Code for ID3v2.2.x tag }
TAG_VERSION_2_3 = 3; { Code for ID3v2.3.x tag }
TAG_VERSION_2_4 = 4; { Code for ID3v2.4.x tag }
type
{ Class TID3v2 }
TID3v2 = class(TObject)
private
{ Private declarations }
FExists: Boolean;
FVersionID: Byte;
FSize: Integer;
FTitle: string;
FArtist: string;
FAlbum: string;
FTrack: Byte;
FYear: string;
FGenre: string;
FComment: string;
FComposer: string;
FEncoder: string;
FCopyright: string;
FLanguage: string;
FLink: string;
procedure FSetTitle(const NewTitle: string);
procedure FSetArtist(const NewArtist: string);
procedure FSetAlbum(const NewAlbum: string);
procedure FSetTrack(const NewTrack: Byte);
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
{ Public declarations }
constructor Create; { Create object }
procedure ResetData; { Reset all data }
function ReadFromFile(const FileName: string): Boolean; { Load tag }
function SaveToFile(const FileName: string): Boolean; { Save tag }
function RemoveFromFile(const FileName: string): Boolean; { Delete tag }
property Exists: Boolean read FExists; { True if tag found }
property VersionID: Byte read FVersionID; { Version code }
property Size: Integer read FSize; { Total tag size }
property Title: string read FTitle write FSetTitle; { Song title }
property Artist: string read FArtist write FSetArtist; { Artist name }
property Album: string read FAlbum write FSetAlbum; { Album title }
property Track: Byte read FTrack write FSetTrack; { Track number }
property Year: string read FYear write FSetYear; { Release year }
property Genre: string read FGenre write FSetGenre; { Genre name }
property Comment: string read FComment write FSetComment; { Comment }
property Composer: string read FComposer write FSetComposer; { Composer }
property Encoder: string read FEncoder write FSetEncoder; { Encoder }
property Copyright: string read FCopyright write FSetCopyright; { (c) }
property Language: string read FLanguage write FSetLanguage; { Language }
property Link: string read FLink write FSetLink; { URL link }
end;
implementation
const
{ ID3v2 tag ID }
ID3V2_ID = 'ID3';
{ Max. number of supported tag frames }
ID3V2_FRAME_COUNT = 16;
{ Names of supported tag frames (ID3v2.3.x & ID3v2.4.x) }
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');
{ Names of supported tag frames (ID3v2.2.x) }
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');
type
{ Frame header (ID3v2.3.x & ID3v2.4.x) }
FrameHeaderNew = record
ID: array [1..4] of Char; { Frame ID }
Size: Integer; { Size excluding header }
Flags: Word; { Flags }
end;
{ Frame header (ID3v2.2.x) }
FrameHeaderOld = record
ID: array [1..3] of Char; { Frame ID }
Size: array [1..3] of Byte; { Size excluding header }
end;
{ ID3v2 header data - for internal use }
TagInfo = record
{ Real structure of ID3v2 header }
ID: array [1..3] of Char; { Always "ID3" }
Version: Byte; { Version number }
Revision: Byte; { Revision number }
Flags: Byte; { Flags of tag }
Size: array [1..4] of Byte; { Tag size excluding header }
{ Extended data }
FileSize: Integer; { File size (bytes) }
Frame: array [1..ID3V2_FRAME_COUNT] of string; { Information from frames }
end;
{ ********************* Auxiliary functions & procedures ******************** }
function ReadHeader(const FileName: string; var Tag: TagInfo): Boolean;
var
SourceFile: file;
Transferred: Integer;
begin
try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
{ Read header and get file size }
BlockRead(SourceFile, Tag, 10, Transferred);
Tag.FileSize := FileSize(SourceFile);
CloseFile(SourceFile);
{ if transfer is not complete }
if Transferred < 10 then Result := false;
except
{ Error }
Result := false;
end;
end;
{ --------------------------------------------------------------------------- }
function GetTagSize(const Tag: TagInfo): Integer;
begin
{ Get total tag size }
Result :=
Tag.Size[1] * $200000 +
Tag.Size[2] * $4000 +
Tag.Size[3] * $80 +
Tag.Size[4] + 10;
if Tag.Flags and $10 > 0 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
{ Set tag item if supported frame found }
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 then Tag.Frame[Iterator] := Data;
end;
end;
{ --------------------------------------------------------------------------- }
function Swap32(const Figure: Integer): Integer;
var
ByteArray: array [1..4] of Byte absolute Figure;
begin
{ Swap 4 bytes }
Result :=
ByteArray[1] * $1000000 +
ByteArray[2] * $10000 +
ByteArray[3] * $100 +
ByteArray[4];
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesNew(const FileName: string; var Tag: TagInfo);
var
SourceFile: file;
Frame: FrameHeaderNew;
Data: array [1..250] of Char;
DataPosition, DataSize: Integer;
begin
{ Get information from frames (ID3v2.3.x & ID3v2.4.x) }
try
{ Set read-access, open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
Seek(SourceFile, 10);
while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
begin
FillChar(Data, SizeOf(Data), 0);
{ Read frame header and check frame ID }
BlockRead(SourceFile, Frame, 10);
if not (Frame.ID[1] in ['A'..'Z']) then break;
{ Note data position and determine significant data size }
DataPosition := FilePos(SourceFile);
if Swap32(Frame.Size) > SizeOf(Data) then DataSize := SizeOf(Data)
else DataSize := Swap32(Frame.Size);
{ Read frame data and set tag item if frame supported }
BlockRead(SourceFile, Data, DataSize);
SetTagItem(Frame.ID, Data, Tag);
Seek(SourceFile, DataPosition + Swap32(Frame.Size));
end;
CloseFile(SourceFile);
except
end;
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesOld(const FileName: string; var Tag: TagInfo);
var
SourceFile: file;
Frame: FrameHeaderOld;
Data: array [1..250] of Char;
DataPosition, FrameSize, DataSize: Integer;
begin
{ Get information from frames (ID3v2.2.x) }
try
{ Set read-access, open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
Seek(SourceFile, 10);
while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
begin
FillChar(Data, SizeOf(Data), 0);
{ Read frame header and check frame ID }
BlockRead(SourceFile, Frame, 6);
if not (Frame.ID[1] in ['A'..'Z']) then break;
{ Note data position and determine significant data size }
DataPosition := FilePos(SourceFile);
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;
{ Read frame data and set tag item if frame supported }
BlockRead(SourceFile, Data, DataSize);
SetTagItem(Frame.ID, Data, Tag);
Seek(SourceFile, DataPosition + FrameSize);
end;
CloseFile(SourceFile);
except
end;
end;
{ --------------------------------------------------------------------------- }
function GetContent(const Content1, Content2: string): string;
begin
{ Get content preferring the first content }
Result := Trim(Content1);
if Result = '' then Result := Trim(Content2);
end;
{ --------------------------------------------------------------------------- }
function ExtractTrack(const TrackString: string): Byte;
var
Index, Value, Code: Integer;
begin
{ Extract track from string }
Index := Pos('/', Trim(TrackString));
if Index = 0 then Val(Trim(TrackString), Value, Code)
else Val(Copy(Trim(TrackString), 1, Index - 1), Value, Code);
if Code = 0 then Result := Value
else Result := 0;
end;
{ --------------------------------------------------------------------------- }
function ExtractYear(const YearString, DateString: string): string;
begin
{ Extract year from strings }
Result := Trim(YearString);
if Result = '' then Result := Copy(Trim(DateString), 1, 4);
end;
{ --------------------------------------------------------------------------- }
function ExtractGenre(const GenreString: string): string;
begin
{ Extract genre from string }
Result := Trim(GenreString);
if Pos(')', Result) > 0 then Delete(Result, 1, LastDelimiter(')', Result));
end;
{ --------------------------------------------------------------------------- }
function ExtractComment(const CommentString: string): string;
var
Comment: string;
begin
{ Extract comment from string }
Comment := CommentString;
Delete(Comment, 1, 4);
Delete(Comment, 1, Pos(#0, Comment));
Result := Trim(Comment);
end;
{ --------------------------------------------------------------------------- }
function ExtractLink(const LinkString: string): string;
var
Link: string;
begin
{ Extract URL link from string }
Link := LinkString;
Delete(Link, 1, 1);
Delete(Link, 1, Pos(#0, Link));
Result := Trim(Link);
end;
{ --------------------------------------------------------------------------- }
procedure BuildHeader(var Tag: TagInfo);
var
Iterator, TagSize: Integer;
begin
{ Build tag header }
Tag.ID := ID3V2_ID;
Tag.Version := TAG_VERSION_2_3;
Tag.Revision := 0;
Tag.Flags := 0;
TagSize := 0;
for Iterator := 1 to ID3V2_FRAME_COUNT do
if Tag.Frame[Iterator] <> '' then
Inc(TagSize, Length(Tag.Frame[Iterator]) + 11);
{ Convert tag size }
Tag.Size[1] := TagSize div $200000;
Tag.Size[2] := TagSize div $4000;
Tag.Size[3] := TagSize div $80;
Tag.Size[4] := TagSize mod $80;
end;
{ --------------------------------------------------------------------------- }
function RebuildFile(const FileName: string; TagData: TStream): Boolean;
var
Tag: TagInfo;
Source, Destination: TFileStream;
BufferName: string;
begin
{ Rebuild file with old file data and new tag data (optional) }
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
{ Create file streams }
BufferName := FileName + '~';
Source := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
Destination := TFileStream.Create(BufferName, fmCreate);
{ Copy data blocks }
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);
{ Free resources }
Source.Free;
Destination.Free;
{ Replace old file and delete temporary file }
if (DeleteFile(FileName)) and (RenameFile(BufferName, FileName)) then
Result := true
else
raise Exception.Create('');
except
{ Access error }
if FileExists(BufferName) then DeleteFile(BufferName);
end;
end;
{ --------------------------------------------------------------------------- }
function SaveTag(const FileName: string; Tag: TagInfo): Boolean;
var
TagData: TStringStream;
Iterator, FrameSize: Integer;
begin
{ Build and write tag header and frames to stream }
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;
{ Rebuild file with new tag data }
Result := RebuildFile(FileName, TagData);
TagData.Free;
end;
{ ********************** Private functions & procedures ********************* }
procedure TID3v2.FSetTitle(const NewTitle: string);
begin
{ Set song title }
FTitle := Trim(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetArtist(const NewArtist: string);
begin
{ Set artist name }
FArtist := Trim(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetAlbum(const NewAlbum: string);
begin
{ Set album title }
FAlbum := Trim(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetTrack(const NewTrack: Byte);
begin
{ Set track number }
FTrack := NewTrack;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetYear(const NewYear: string);
begin
{ Set release year }
FYear := Trim(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetGenre(const NewGenre: string);
begin
{ Set genre name }
FGenre := Trim(NewGenre);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComment(const NewComment: string);
begin
{ Set comment }
FComment := Trim(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComposer(const NewComposer: string);
begin
{ Set composer name }
FComposer := Trim(NewComposer);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetEncoder(const NewEncoder: string);
begin
{ Set encoder name }
FEncoder := Trim(NewEncoder);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetCopyright(const NewCopyright: string);
begin
{ Set copyright information }
FCopyright := Trim(NewCopyright);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLanguage(const NewLanguage: string);
begin
{ Set language }
FLanguage := Trim(NewLanguage);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLink(const NewLink: string);
begin
{ Set URL link }
FLink := Trim(NewLink);
end;
{ ********************** Public functions & procedures ********************** }
constructor TID3v2.Create;
begin
{ Create object }
inherited;
ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.ResetData;
begin
{ Reset all variables }
FExists := false;
FVersionID := 0;
FSize := 0;
FTitle := '';
FArtist := '';
FAlbum := '';
FTrack := 0;
FYear := '';
FGenre := '';
FComment := '';
FComposer := '';
FEncoder := '';
FCopyright := '';
FLanguage := '';
FLink := '';
end;
{ --------------------------------------------------------------------------- }
function TID3v2.ReadFromFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Reset data and load header from file to variable }
ResetData;
Result := ReadHeader(FileName, Tag);
{ Process data if loaded and header valid }
if (Result) and (Tag.ID = ID3V2_ID) then
begin
FExists := true;
{ Fill properties with header data }
FVersionID := Tag.Version;
FSize := GetTagSize(Tag);
{ Get information from frames if version supported }
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]);
FYear := ExtractYear(Tag.Frame[5], Tag.Frame[13]);
FGenre := ExtractGenre(Tag.Frame[6]);
FComment := ExtractComment(Tag.Frame[7]);
FComposer := Trim(Tag.Frame[8]);
FEncoder := Trim(Tag.Frame[9]);
FCopyright := Trim(Tag.Frame[10]);
FLanguage := Trim(Tag.Frame[11]);
FLink := ExtractLink(Tag.Frame[12]);
end;
end;
end;
{ --------------------------------------------------------------------------- }
function TID3v2.SaveToFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Prepare tag data and save to file }
FillChar(Tag, SizeOf(Tag), 0);
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: string): Boolean;
begin
{ Remove tag from file }
Result := RebuildFile(FileName, nil);
end;
end.

View File

@@ -0,0 +1,32 @@
ID3v2.pas
Tested with Borland Delphi 3,4,5,6
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TID3v2 - for manipulating with ID3v2 tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.4 (24 March 2002) }
{ - Reading support for ID3v2.2.x & ID3v2.4.x tags }
{ }
{ Version 1.3 (16 February 2002) }
{ - Fixed bug with property Comment }
{ - Added info: composer, encoder, copyright, language, link }
{ }
{ Version 1.2 (17 October 2001) }
{ - Writing support for ID3v2.3.x tags }
{ - Fixed bug with track number detection }
{ - Fixed bug with tag reading }
{ }
{ Version 1.1 (31 August 2001) }
{ - Added public procedure ResetData }
{ }
{ Version 1.0 (14 August 2001) }
{ - Reading support for ID3v2.3.x tags }
{ - Tag info: title, artist, album, track, year, genre, comment }
{ }
{ *************************************************************************** }

Binary file not shown.

View File

@@ -0,0 +1,179 @@
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl, ExtCtrls, ID3v2;
type
TMainForm = class(TForm)
DriveList: TDriveComboBox;
FolderList: TDirectoryListBox;
FileList: TFileListBox;
SaveButton: TButton;
RemoveButton: TButton;
CloseButton: TButton;
InfoBevel: TBevel;
IconImage: TImage;
TagExistsLabel: TLabel;
TagExistsValue: TEdit;
VersionLabel: TLabel;
VersionValue: TEdit;
SizeLabel: TLabel;
SizeValue: TEdit;
TitleLabel: TLabel;
TitleEdit: TEdit;
ArtistLabel: TLabel;
ArtistEdit: TEdit;
AlbumLabel: TLabel;
AlbumEdit: TEdit;
TrackLabel: TLabel;
TrackEdit: TEdit;
YearLabel: TLabel;
YearEdit: TEdit;
GenreLabel: TLabel;
GenreEdit: TEdit;
CommentLabel: TLabel;
CommentEdit: TEdit;
ComposerLabel: TLabel;
ComposerEdit: TEdit;
EncoderLabel: TLabel;
EncoderEdit: TEdit;
CopyrightLabel: TLabel;
CopyrightEdit: TEdit;
LanguageLabel: TLabel;
LanguageEdit: TEdit;
LinkLabel: TLabel;
LinkEdit: TEdit;
procedure FormCreate(Sender: TObject);
procedure FileListChange(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure SaveButtonClick(Sender: TObject);
procedure RemoveButtonClick(Sender: TObject);
procedure CloseButtonClick(Sender: TObject);
private
{ Private declarations }
FileTag: TID3v2;
procedure ClearAll;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.ClearAll;
begin
{ Clear all captions }
TagExistsValue.Text := '';
VersionValue.Text := '';
SizeValue.Text := '';
TitleEdit.Text := '';
ArtistEdit.Text := '';
AlbumEdit.Text := '';
TrackEdit.Text := '';
YearEdit.Text := '';
GenreEdit.Text := '';
CommentEdit.Text := '';
ComposerEdit.Text := '';
EncoderEdit.Text := '';
CopyrightEdit.Text := '';
LanguageEdit.Text := '';
LinkEdit.Text := '';
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Create object and clear captions }
FileTag := TID3v2.Create;
ClearAll;
end;
procedure TMainForm.FileListChange(Sender: TObject);
begin
{ Clear captions }
ClearAll;
if FileList.FileName = '' then exit;
if FileExists(FileList.FileName) then
{ Load tag data }
if FileTag.ReadFromFile(FileList.FileName) then
if FileTag.Exists then
begin
{ Fill captions }
TagExistsValue.Text := 'Yes';
VersionValue.Text := '2.' + IntToStr(FileTag.VersionID);
SizeValue.Text := IntToStr(FileTag.Size) + ' bytes';
TitleEdit.Text := FileTag.Title;
ArtistEdit.Text := FileTag.Artist;
AlbumEdit.Text := FileTag.Album;
if FileTag.Track > 0 then TrackEdit.Text := IntToStr(FileTag.Track);
YearEdit.Text := FileTag.Year;
GenreEdit.Text := FileTag.Genre;
CommentEdit.Text := FileTag.Comment;
ComposerEdit.Text := FileTag.Composer;
EncoderEdit.Text := FileTag.Encoder;
CopyrightEdit.Text := FileTag.Copyright;
LanguageEdit.Text := FileTag.Language;
LinkEdit.Text := FileTag.Link;
end
else
{ Tag not found }
TagExistsValue.Text := 'No'
else
{ Read error }
ShowMessage('Can not read tag from the file: ' + FileList.FileName)
else
{ File does not exist }
ShowMessage('The file does not exist: ' + FileList.FileName);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ Free memory }
FileTag.Free;
end;
procedure TMainForm.SaveButtonClick(Sender: TObject);
var
Value, Code: Integer;
begin
{ Prepare tag data }
FileTag.Title := TitleEdit.Text;
FileTag.Artist := ArtistEdit.Text;
FileTag.Album := AlbumEdit.Text;
Val(TrackEdit.Text, Value, Code);
if (Code = 0) and (Value > 0) then FileTag.Track := Value
else FileTag.Track := 0;
FileTag.Year := YearEdit.Text;
FileTag.Genre := GenreEdit.Text;
FileTag.Comment := CommentEdit.Text;
FileTag.Composer := ComposerEdit.Text;
FileTag.Encoder := EncoderEdit.Text;
FileTag.Copyright := CopyrightEdit.Text;
FileTag.Language := LanguageEdit.Text;
FileTag.Link := LinkEdit.Text;
{ Save tag data }
if (not FileExists(FileList.FileName)) or
(not FileTag.SaveToFile(FileList.FileName)) then
ShowMessage('Can not save tag to the file: ' + FileList.FileName);
FileListChange(Self);
end;
procedure TMainForm.RemoveButtonClick(Sender: TObject);
begin
{ Delete tag data }
if (FileExists(FileList.FileName)) and
(FileTag.RemoveFromFile(FileList.FileName)) then ClearAll
else ShowMessage('Can not remove tag from the file: ' + FileList.FileName);
end;
procedure TMainForm.CloseButtonClick(Sender: TObject);
begin
{ Exit }
Close;
end;
end.

View File

@@ -0,0 +1,35 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-LE"c:\program files\borland\delphi6\Projects\Bpl"
-LN"c:\program files\borland\delphi6\Projects\Bpl"

View File

@@ -0,0 +1,96 @@
[FileVersion]
Version=6.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;dbexpress;dbxcds;indy;dclOffice2k
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Language]
ActiveLang=
ProjectLang=
RootDir=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1031
CodePage=1252
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlUnitOutputDirectory]
Count=1
Item0=DCU
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=Data

View File

@@ -0,0 +1,15 @@
program Test;
uses
Forms,
Main in 'Main.pas' {MainForm},
ID3v2 in 'ID3v2.pas';
{$R *.res}
begin
Application.Initialize;
Application.Title := 'ID3v2 Test';
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

Binary file not shown.

View File

@@ -0,0 +1,30 @@
Monkey.pas (ID3v1.pas, ID3v2.pas, APEtag.pas needed)
Tested with Borland Delphi 3,4,5,6
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TMonkey - for manipulating with Monkey's Audio file information }
{ }
{ Uses: }
{ - Class TID3v1 }
{ - Class TID3v2 }
{ - Class TAPEtag }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.2 (21 April 2002) }
{ - Class TID3v2: support for ID3v2 tags }
{ - Class TAPEtag: support for APE tags }
{ }
{ Version 1.1 (11 September 2001) }
{ - Added property Samples }
{ - Removed WAV header information }
{ }
{ Version 1.0 (7 September 2001) }
{ - Support for Monkey's Audio files }
{ - Class TID3v1: reading & writing support for ID3v1.x tags }
{ }
{ *************************************************************************** }

Binary file not shown.

View File

@@ -0,0 +1,131 @@
unit Main;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, FileCtrl, ExtCtrls, Monkey;
type
TMainForm = class(TForm)
DriveList: TDriveComboBox;
FolderList: TDirectoryListBox;
FileList: TFileListBox;
CloseButton: TButton;
InfoBevel: TBevel;
IconImage: TImage;
ValidHeaderLabel: TLabel;
FileLengthLabel: TLabel;
ValidHeaderValue: TEdit;
ChannelModeValue: TEdit;
ChannelModeLabel: TLabel;
SampleRateLabel: TLabel;
BitsPerSampleLabel: TLabel;
DurationLabel: TLabel;
SampleRateValue: TEdit;
BitsPerSampleValue: TEdit;
DurationValue: TEdit;
FileLengthValue: TEdit;
CompressionLabel: TLabel;
CompressionValue: TEdit;
PeakLevelLabel: TLabel;
PeakLevelValue: TEdit;
FramesLabel: TLabel;
FramesValue: TEdit;
FlagsLabel: TLabel;
FlagsValue: TEdit;
SeekElementsLabel: TLabel;
SeekElementsValue: TEdit;
TotalSamplesLabel: TLabel;
TotalSamplesValue: TEdit;
procedure CloseButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FileListChange(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
Monkey: TMonkey;
procedure ClearAll;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
procedure TMainForm.ClearAll;
begin
{ Clear all captions }
ValidHeaderValue.Text := '';
FileLengthValue.Text := '';
ChannelModeValue.Text := '';
SampleRateValue.Text := '';
BitsPerSampleValue.Text := '';
DurationValue.Text := '';
FlagsValue.Text := '';
FramesValue.Text := '';
TotalSamplesValue.Text := '';
PeakLevelValue.Text := '';
SeekElementsValue.Text := '';
CompressionValue.Text := '';
end;
procedure TMainForm.CloseButtonClick(Sender: TObject);
begin
{ Exit }
Close;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
{ Create object and reset captions }
Monkey := TMonkey.Create;
ClearAll;
end;
procedure TMainForm.FileListChange(Sender: TObject);
begin
{ Clear captions }
ClearAll;
if FileList.FileName = '' then exit;
if FileExists(FileList.FileName) then
{ Load Monkey's Audio data }
if Monkey.ReadFromFile(FileList.FileName) then
if Monkey.Valid then
begin
{ Fill captions }
ValidHeaderValue.Text := 'Yes, version ' + Monkey.Version;
FileLengthValue.Text := IntToStr(Monkey.FileLength) + ' bytes';
ChannelModeValue.Text := Monkey.ChannelMode;
SampleRateValue.Text := IntToStr(Monkey.Header.SampleRate) + ' hz';
BitsPerSampleValue.Text := IntToStr(Monkey.Bits) + ' bit';
DurationValue.Text := FormatFloat('.000', Monkey.Duration) + ' sec.';
FlagsValue.Text := IntToStr(Monkey.Header.Flags);
FramesValue.Text := IntToStr(Monkey.Header.Frames);
TotalSamplesValue.Text := IntToStr(Monkey.Samples);
PeakLevelValue.Text := FormatFloat('00.00', Monkey.Peak) + '% - ' +
IntToStr(Monkey.Header.PeakLevel);
SeekElementsValue.Text := IntToStr(Monkey.Header.SeekElements);
CompressionValue.Text := FormatFloat('00.00', Monkey.Ratio) + '% - ' +
Monkey.Compression;
end
else
{ Header not found }
ValidHeaderValue.Text := 'No'
else
{ Read error }
ShowMessage('Can not read header in the file: ' + FileList.FileName)
else
{ File does not exist }
ShowMessage('The file does not exist: ' + FileList.FileName);
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
{ Free memory }
Monkey.Free;
end;
end.

View File

@@ -0,0 +1,300 @@
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TMonkey - for manipulating with Monkey's Audio file information }
{ }
{ Uses: }
{ - Class TID3v1 }
{ - Class TID3v2 }
{ - Class TAPEtag }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.2 (21 April 2002) }
{ - Class TID3v2: support for ID3v2 tags }
{ - Class TAPEtag: support for APE tags }
{ }
{ Version 1.1 (11 September 2001) }
{ - Added property Samples }
{ - Removed WAV header information }
{ }
{ Version 1.0 (7 September 2001) }
{ - Support for Monkey's Audio files }
{ - Class TID3v1: reading & writing support for ID3v1.x tags }
{ }
{ *************************************************************************** }
unit Monkey;
interface
uses
Classes, SysUtils, ID3v1, ID3v2, APEtag;
const
{ Compression level codes }
MONKEY_COMPRESSION_FAST = 1000; { Fast (poor) }
MONKEY_COMPRESSION_NORMAL = 2000; { Normal (good) }
MONKEY_COMPRESSION_HIGH = 3000; { High (very good) }
MONKEY_COMPRESSION_EXTRA_HIGH = 4000; { Extra high (best) }
{ Compression level names }
MONKEY_COMPRESSION: array [0..4] of string =
('Unknown', 'Fast', 'Normal', 'High', 'Extra High');
{ Format flags }
MONKEY_FLAG_8_BIT = 1; { Audio 8-bit }
MONKEY_FLAG_CRC = 2; { New CRC32 error detection }
MONKEY_FLAG_PEAK_LEVEL = 4; { Peak level stored }
MONKEY_FLAG_24_BIT = 8; { Audio 24-bit }
MONKEY_FLAG_SEEK_ELEMENTS = 16; { Number of seek elements stored }
MONKEY_FLAG_WAV_NOT_STORED = 32; { WAV header not stored }
{ Channel mode names }
MONKEY_MODE: array [0..2] of string =
('Unknown', 'Mono', 'Stereo');
type
{ Real structure of Monkey's Audio header }
MonkeyHeader = record
ID: array [1..4] of Char; { Always "MAC " }
VersionID: Word; { Version number * 1000 (3.91 = 3910) }
CompressionID: Word; { Compression level code }
Flags: Word; { Any format flags }
Channels: Word; { Number of channels }
SampleRate: Integer; { Sample rate (hz) }
HeaderBytes: Integer; { Header length (without header ID) }
TerminatingBytes: Integer; { Extended data }
Frames: Integer; { Number of frames in the file }
FinalSamples: Integer; { Number of samples in the final frame }
PeakLevel: Integer; { Peak level (if stored) }
SeekElements: Integer; { Number of seek elements (if stored) }
end;
{ Class TMonkey }
TMonkey = class(TObject)
private
{ Private declarations }
FFileLength: Integer;
FHeader: MonkeyHeader;
FID3v1: TID3v1;
FID3v2: TID3v2;
FAPEtag: TAPEtag;
procedure FResetData;
function FGetValid: Boolean;
function FGetVersion: string;
function FGetCompression: string;
function FGetBits: Byte;
function FGetChannelMode: string;
function FGetPeak: Double;
function FGetSamplesPerFrame: Integer;
function FGetSamples: Integer;
function FGetDuration: Double;
function FGetRatio: Double;
public
{ Public declarations }
constructor Create; { Create object }
destructor Destroy; override; { Destroy object }
function ReadFromFile(const FileName: string): Boolean; { Load header }
property FileLength: Integer read FFileLength; { File length (bytes) }
property Header: MonkeyHeader read FHeader; { Monkey's Audio header }
property ID3v1: TID3v1 read FID3v1; { ID3v1 tag data }
property ID3v2: TID3v2 read FID3v2; { ID3v2 tag data }
property APEtag: TAPEtag read FAPEtag; { APE tag data }
property Valid: Boolean read FGetValid; { True if header valid }
property Version: string read FGetVersion; { Encoder version }
property Compression: string read FGetCompression; { Compression level }
property Bits: Byte read FGetBits; { Bits per sample }
property ChannelMode: string read FGetChannelMode; { Channel mode }
property Peak: Double read FGetPeak; { Peak level ratio (%) }
property Samples: Integer read FGetSamples; { Number of samples }
property Duration: Double read FGetDuration; { Duration (seconds) }
property Ratio: Double read FGetRatio; { Compression ratio (%) }
end;
implementation
{ ********************** Private functions & procedures ********************* }
procedure TMonkey.FResetData;
begin
{ Reset data }
FFileLength := 0;
FillChar(FHeader, SizeOf(FHeader), 0);
FID3v1.ResetData;
FID3v2.ResetData;
FAPEtag.ResetData;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetValid: Boolean;
begin
{ Check for right Monkey's Audio file data }
Result :=
(FHeader.ID = 'MAC ') and
(FHeader.SampleRate > 0) and
(FHeader.Channels > 0);
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetVersion: string;
begin
{ Get encoder version }
if FHeader.VersionID = 0 then Result := ''
else Str(FHeader.VersionID / 1000 : 4 : 2, Result);
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetCompression: string;
begin
{ Get compression level }
Result := MONKEY_COMPRESSION[FHeader.CompressionID div 1000];
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetBits: Byte;
begin
{ Get number of bits per sample }
if FGetValid then
begin
Result := 16;
if FHeader.Flags and MONKEY_FLAG_8_BIT > 0 then Result := 8;
if FHeader.Flags and MONKEY_FLAG_24_BIT > 0 then Result := 24;
end
else
Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetChannelMode: string;
begin
{ Get channel mode }
Result := MONKEY_MODE[FHeader.Channels];
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetPeak: Double;
begin
{ Get peak level ratio }
if (FGetValid) and (FHeader.Flags and MONKEY_FLAG_PEAK_LEVEL > 0) then
case FGetBits of
16: Result := FHeader.PeakLevel / 32768 * 100;
24: Result := FHeader.PeakLevel / 8388608 * 100;
else Result := FHeader.PeakLevel / 128 * 100;
end
else
Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetSamplesPerFrame: Integer;
begin
{ Get number of samples in a frame }
if FGetValid then
if (FHeader.VersionID >= 3900) or
((FHeader.VersionID >= 3800) and
(FHeader.CompressionID = MONKEY_COMPRESSION_EXTRA_HIGH)) then
Result := 73728
else
Result := 9216
else
Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetSamples: Integer;
begin
{ Get number of samples }
if FGetValid then
Result := (FHeader.Frames - 1) * FGetSamplesPerFrame + FHeader.FinalSamples
else
Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetDuration: Double;
begin
{ Get song duration }
if FGetValid then Result := FGetSamples / FHeader.SampleRate
else Result := 0;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.FGetRatio: Double;
begin
{ Get compression ratio }
if FGetValid then
Result := FFileLength /
(FGetSamples * FHeader.Channels * FGetBits / 8 + 44) * 100
else
Result := 0;
end;
{ ********************** Public functions & procedures ********************** }
constructor TMonkey.Create;
begin
{ Create object }
inherited;
FID3v1 := TID3v1.Create;
FID3v2 := TID3v2.Create;
FAPEtag := TAPEtag.Create;
FResetData;
end;
{ --------------------------------------------------------------------------- }
destructor TMonkey.Destroy;
begin
{ Destroy object }
FID3v1.Free;
FID3v2.Free;
FAPEtag.Free;
inherited;
end;
{ --------------------------------------------------------------------------- }
function TMonkey.ReadFromFile(const FileName: string): Boolean;
var
SourceFile: file;
begin
try
{ Reset data and search for file tag }
FResetData;
if (not FID3v1.ReadFromFile(FileName)) or
(not FID3v2.ReadFromFile(FileName)) or
(not FAPEtag.ReadFromFile(FileName)) then raise Exception.Create('');
{ Set read-access, open file and get file length }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
FFileLength := FileSize(SourceFile);
{ Read Monkey's Audio header data }
Seek(SourceFile, ID3v2.Size);
BlockRead(SourceFile, FHeader, SizeOf(FHeader));
if FHeader.Flags and MONKEY_FLAG_PEAK_LEVEL = 0 then
FHeader.PeakLevel := 0;
if FHeader.Flags and MONKEY_FLAG_SEEK_ELEMENTS = 0 then
FHeader.SeekElements := 0;
CloseFile(SourceFile);
Result := true;
except
FResetData;
Result := false;
end;
end;
end.

View File

@@ -0,0 +1,35 @@
-$A8
-$B-
-$C+
-$D+
-$E-
-$F-
-$G+
-$H+
-$I+
-$J-
-$K-
-$L+
-$M-
-$N+
-$O+
-$P+
-$Q-
-$R-
-$S-
-$T-
-$U-
-$V+
-$W-
-$X+
-$YD
-$Z1
-cg
-AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
-H+
-W+
-M
-$M16384,1048576
-K$00400000
-LE"c:\program files\borland\delphi6\Projects\Bpl"
-LN"c:\program files\borland\delphi6\Projects\Bpl"

View File

@@ -0,0 +1,96 @@
[FileVersion]
Version=6.0
[Compiler]
A=8
B=0
C=1
D=1
E=0
F=0
G=1
H=1
I=1
J=0
K=0
L=1
M=0
N=1
O=1
P=1
Q=0
R=0
S=0
T=0
U=0
V=1
W=0
X=1
Y=1
Z=1
ShowHints=1
ShowWarnings=1
UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[Linker]
MapFile=0
OutputObjs=0
ConsoleApp=1
DebugInfo=0
RemoteSymbols=0
MinStackSize=16384
MaxStackSize=1048576
ImageBase=4194304
ExeDescription=
[Directories]
OutputDir=
UnitOutputDir=
PackageDLLOutputDir=
PackageDCPOutputDir=
SearchPath=
Packages=vcl;rtl;dbrtl;adortl;vcldb;vclx;bdertl;vcldbx;ibxpress;dsnap;cds;bdecds;qrpt;teeui;teedb;tee;dss;teeqr;visualclx;visualdbclx;dsnapcrba;dsnapcon;VclSmp;vclshlctrls;vclie;xmlrtl;inet;inetdbbde;inetdbxpress;inetdb;nmfast;webdsnap;websnap;dbexpress;dbxcds;indy;dclOffice2k
Conditionals=
DebugSourceDirs=
UsePackages=0
[Parameters]
RunParams=
HostApplication=
Launcher=
UseLauncher=0
DebugCWD=
[Language]
ActiveLang=
ProjectLang=
RootDir=
[Version Info]
IncludeVerInfo=0
AutoIncBuild=0
MajorVer=1
MinorVer=0
Release=0
Build=0
Debug=0
PreRelease=0
Special=0
Private=0
DLL=0
Locale=1031
CodePage=1252
[Version Info Keys]
CompanyName=
FileDescription=
FileVersion=1.0.0.0
InternalName=
LegalCopyright=
LegalTrademarks=
OriginalFilename=
ProductName=
ProductVersion=1.0.0.0
Comments=
[HistoryLists\hlUnitAliases]
Count=1
Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;
[HistoryLists\hlUnitOutputDirectory]
Count=1
Item0=DCU
[HistoryLists\hlOutputDirectorry]
Count=1
Item0=Data

View File

@@ -0,0 +1,18 @@
program Test;
uses
Forms,
Main in 'Main.pas' {MainForm},
Monkey in 'Monkey.pas',
ID3v1 in '..\ID3v1\ID3v1.pas',
ID3v2 in '..\ID3v2\ID3v2.pas',
APEtag in '..\APEtag\APEtag.pas';
{$R *.res}
begin
Application.Initialize;
Application.Title := 'Monkey Test';
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
TMonkey Delphi Class for analyzing APE files
Sumbitted by Jurgen Faul ( jfaul@gmx.de )
(note: this does not directly use the MAC SDK, so it is not guaranteed to work with future APE files)