Umíme to s Delphi: 137. díl – ID3 tagy prakticky

Dnešní článek je zaměřen na praktickou práci s ID3 tagy. Pomocí ID3 tagů můžeme rozšířit hudební soubor MP3 o možnost uchovávat textové informace (název skladby, jméno interpreta, rok vydání apod.). Ve článku společně vytvoříme aplikaci, která dokáže přečíst tyto informace z existujícícho souboru MP3 a zobrazit je v průběhu přehrávání skladby.

V dnešním článku navážeme na problematiku otevřenou minule. Před týdnem jsme se totiž začali zabývat technologií umožňující připojit k hudebním MP3 souborům textové informace a s jejich pomocí uchovávat údaje typu název skladby, jméno autora či interpreta, rok vydání, žánr apod. V článku jsme si teoreticky popsali historii ID3 tagů, jejich význam a existující verze. Následně jsme se velmi podrobně zabývali první a druhou verzí tagů – ID3v1, resp. ID3v1.1.

V dnešním článku se nejprve podíváme na praktickou implementaci ID3 tagů v Delphi. Vytvoříme společně jednoduchou aplikaci, která bude umožňovat přehrávání MP3 souborů a čtení jejich ID3 tagů. Pokud zbyde čas, podíváme se v závěru článku na další, podstatně sofistikovanější verzi ID3 tagů – ID3v2. Pokud ne, necháme si tuto verzi na příště.

Implementace ID3 tagů (verze 1) v Delphi

Na úplný úvod si připomeňme, co jsme si o ID3 tagu řekli posledně. Jedná se o strukturu s následujícím obsahem:

Údaj Délka (počet bajtů)
Název skladby 30 znaků (bajtů)
Jméno interpreta 30 znaků (bajtů)
Název alba 30 znaků (bajtů)
Rok vydání 4 znaky (bajty)
Komentář (další údaje) 30 znaků (bajtů)
Žánr 1 bajt

Z toho také plyne jedna z možných Delphi implementací. Vzpomenem-li si navíc na to, že první tři znaky celého tagu jsou vyhrazeny pro řetězec „TAG“, můžeme pri práci s ID3 tagy v Delphi s výhodou použít například následující strukturu:

type TID3tag_typ = record
  hlavicka: array[0..2] of char;
  nazev: array[0..29] of char;
  interpret: array[0..29] of char;
  album: array[0..29] of char;
  rok: array[0..3] of char;
  komentar: array[0..29] of char;
  zanr: byte;
end;

Jak jsme si řekli posledně, informace o žánru empétrojky je uložena pouze na jednou bajtu. Následující deklarace prozradí, jak z číselného označení žánru dostaneme textový popis:

const CelkemZanru = 147;

ID3zanr: array[0..CelkemZanru] of string = (
    `Blues`, `Classic Rock`, `Country`, `Dance`, `Disco`, `Funk`, `Grunge`,
    `Hip-Hop`, `Jazz`, `Metal`, `New Age`, `Oldies`, `Other`, `Pop`, `R&B`,
    `Rap`, `Reggae`, `Rock`, `Techno`, `Industrial`, `Alternative`, `Ska`,
    `Death Metal`, `Pranks`, `Soundtrack`, `Euro-Techno`, `Ambient`,
    `Trip-Hop`, `Vocal`, `Jazz+Funk`, `Fusion`, `Trance`, `Classical`,
    `Instrumental`, `Acid`, `House`, `Game`, `Sound Clip`, `Gospel`,
    `Noise`, `AlternRock`, `Bass`, `Soul`, `Punk`, `Space`, `Meditative`,
    `Instrumental Pop`, `Instrumental Rock`, `Ethnic`, `Gothic`,
    `Darkwave`, `Techno-Industrial`, `Electronic`, `Pop-Folk`,
    `Eurodance`, `Dream`, `Southern Rock`, `Comedy`, `Cult`, `Gangsta`,
    `Top 40`, `Christian Rap`, `Pop/Funk`, `Jungle`, `Native American`,
    `Cabaret`, `New Wave`, `Psychadelic`, `Rave`, `Showtunes`, `Trailer`,
    `Lo-Fi`, `Tribal`, `Acid Punk`, `Acid Jazz`, `Polka`, `Retro`,
    `Musical`, `Rock & Roll`, `Hard Rock`, `Folk`, `Folk-Rock`,
    `National Folk`, `Swing`, `Fast Fusion`, `Bebob`, `Latin`, `Revival`,
    `Celtic`, `Bluegrass`, `Avantgarde`, `Gothic Rock`, `Progressive Rock`,
    `Psychedelic Rock`, `Symphonic Rock`, `Slow Rock`, `Big Band`,
    `Chorus`, `Easy Listening`, `Acoustic`, `Humour`, `Speech`, `Chanson`,
    `Opera`, `Chamber Music`, `Sonata`, `Symphony`, `Booty Bass`, `Primus`,
    `Porn Groove`, `Satire`, `Slow Jam`, `Club`, `Tango`, `Samba`,
    `Folklore`, `Ballad`, `Power Ballad`, `Rhythmic Soul`, `Freestyle`,
    `Duet`, `Punk Rock`, `Drum Solo`, `Acapella`, `Euro-House`, `Dance Hall`,
    `Goa`, `Drum & Bass`, `Club-House`, `Hardcore`, `Terror`, `Indie`,
    `BritPop`, `Negerpunk`, `Polsk Punk`, `Beat`, `Christian Gangsta Rap`,
    `Heavy Metal`, `Black Metal`, `Crossover`, `Contemporary Christian`,
    `Christian Rock`, `Merengue`, `Salsa`, `Trash Metal`, `Anime`, `Jpop`,
    `Synthpop` 
  );

V okamžiku, kdy budeme chtít implementovat práci s ID3 tagy, bude náš úkol spočívat zejména v následujících operacích:

  • v okamžiku otevření MP3 souboru budeme potřebovat naplnit strukturu odpovídajícími údaji
  • pokud budeme chtít umožnit modifikaci ID3 tagů, bude nutné implementovat aktualizační rutinu pro změnu údajů a pro uložení nových informací do MP3 souboru.

Pojďme se společně podívat na implementaci těchto klíčových kroků.

Čtení ID3 tagu z empétrojky

Proceduru pro čtení MP3 tagu z empétrojky si budeme muset implementovat sami, protože Delphi standardně žádnou takovou funkci nenabízí. Vzhledem k tomu, že známe přesnou strukturu tagu a známe i jeho umístění v souboru, nebude celý úkol neřešitelný.

Jedna z možných implementací je uvedena na následujícím programovém výpisu:

Procedure PrectiID3tag(z_jakeho_souboru: string; var ID3tag: TID3tag_typ);
Var
  fmp3: TFileStream;
begin
  fmp3:=TFileStream.Create(z_jakeho_souboru, fmOpenRead);
  try
    fmp3.position:=fmp3.size-128;
    fmp3.Read(ID3tag,SizeOf(ID3tag));
  finally
    fmp3.free;
  end;
end;

Poté, co zavoláme tuto proceduru a v parametrech jí předáme název MP3 souboru a proměnnou pro uložení ID3 tagu, měli bychom mít k dispozici všechny potřebné údaje informace pro zobrazení informací o MP3 souboru. Následná procedura, která informace zobrazí, může pak vypadat například takto (předpokládejme existenci editačních polí Edit1 až Edit6 pro zobrazení informací z ID3 tagu):

Procedure ZobrazID3tag(ID3: TID3tag_typ);
begin
if ID3.Hlavicka <> `TAG` then begin
   Form1.Edit1.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit2.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit3.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit4.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit5.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit6.Text:=`Neplatna nebo neexistujici ID3 informace`;
 end else begin
   Form1.Edit1.Text:=ID3.nazev;
   Form1.Edit2.Text:=ID3.interpret;
   Form1.Edit3.Text:=ID3.album;
   Form1.Edit4.Text:=ID3.rok;
   Form1.Edit5.Text:=ID3.komentar;

   if ID3.Zanr in [0..CelkemZanru] then
     Form1.Edit6.Text:=ID3Zanr[ID3.Zanr]
   else
     Form1.Edit6.Text:=IntToStr(ID3.Zanr);
 end;
end;

Tímto postupem bychom měli mít ošetřeny všechny klíčové operace s ID3 tagem s výjimkou zapisování tagů do souboru, na které se podíváme za týden. V následující kapitole společně vytvoříme jednoduchou aplikaci, která uvedené procedury použije.

Ukázková aplikace

Vytvořte v Delphi novou aplikaci. Úvodní kroky budou stejné jako při vytváření našeho jdnoduchého MP3 playeru, kterým jsme se zaobírali ve 135. díle seriálu - na formulář umístíme následující komponenty:

  • MediaPlayer ze záložky System,
  • OpenDialog ze záložky Dialogs,
  • Button ze záložky Standard,
  • 6 nápisů (komponent Label) ze záložky Standard.

Nyní ošetříme událost OnCreate hlavního formuláře:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := `Prehravac MP3 :)`;
  MediaPlayer1.VisibleButtons := [btPlay, btPause, btStop];
  Button1.Caption := `Otevri`;

  Label1.Caption := `Nazev`;
  Label2.Caption := `Interpret`;
  Label3.Caption := `Album`;
  Label4.Caption := `Rok`;
  Label5.Caption := `Komentar`;
  Label6.Caption := `Zanr`;
end;

Dále ošetříme událost OnClick komponenty Buton1:

procedure TForm1.Button1Click(Sender: TObject);
var ID3tag: TID3Tag_typ;
begin
  if OpenDialog1.Execute then begin
    MediaPlayer1.Close;
    MediaPlayer1.FileName := OpenDialog1.FileName;
    MediaPlayer1.Open;
  end;
end;

Nyní aplikaci rozšíříme o možnost zpracovávání ID3 tagů. Přidejme tedy na formulář 6 editačních polí (komponent Edit ze záložky Standard). Následně rozšíříme obsluhu události OnClick tlačítka Button1 o rutinu zajišťující zobrazení ID3 tagu v editačních polích. Využijeme přitom procedury vytvořené v úvodnu dnešního článku. Obsluha Button1Click bude tedy potom vypadat takto:

procedure TForm1.Button1Click(Sender: TObject);
var ID3tag: TID3Tag_typ;
begin
  if OpenDialog1.Execute then begin
    MediaPlayer1.Close;
    MediaPlayer1.FileName := OpenDialog1.FileName;
    PrectiID3tag(OpenDialog1.FileName, ID3tag);
    ZobrazID3tag(ID3tag);
    MediaPlayer1.Open;
  end;
end;

Pokud nyní aplikaci zkompilujete a spustíte, po klepnutí na tlačítko a po vybrání souboru MP3 by se měla editační políčka vyplnit informacemi o příslušném hudebním souboru. Funguje?

Zdrojový kód

Závěrem kapitoly si uvedeme kompletní zdrojový kód dnes vytvořené aplikace:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MPlayer;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    Edit6: TEdit;
    MediaPlayer1: TMediaPlayer;
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type TID3tag_typ = record
  hlavicka: array[0..2] of char;
  nazev: array[0..29] of char;
  interpret: array[0..29] of char;
  album: array[0..29] of char;
  rok: array[0..3] of char;
  komentar: array[0..29] of char;
  zanr: byte;
end;


const CelkemZanru = 147;

var ID3zanr: array[0..CelkemZanru] of string = (
    `Blues`, `Classic Rock`, `Country`, `Dance`, `Disco`, `Funk`, `Grunge`,
    `Hip-Hop`, `Jazz`, `Metal`, `New Age`, `Oldies`, `Other`, `Pop`, `R&B`,
    `Rap`, `Reggae`, `Rock`, `Techno`, `Industrial`, `Alternative`, `Ska`,
    `Death Metal`, `Pranks`, `Soundtrack`, `Euro-Techno`, `Ambient`,
    `Trip-Hop`, `Vocal`, `Jazz+Funk`, `Fusion`, `Trance`, `Classical`,
    `Instrumental`, `Acid`, `House`, `Game`, `Sound Clip`, `Gospel`,
    `Noise`, `AlternRock`, `Bass`, `Soul`, `Punk`, `Space`, `Meditative`,
    `Instrumental Pop`, `Instrumental Rock`, `Ethnic`, `Gothic`,
    `Darkwave`, `Techno-Industrial`, `Electronic`, `Pop-Folk`,
    `Eurodance`, `Dream`, `Southern Rock`, `Comedy`, `Cult`, `Gangsta`,
    `Top 40`, `Christian Rap`, `Pop/Funk`, `Jungle`, `Native American`,
    `Cabaret`, `New Wave`, `Psychadelic`, `Rave`, `Showtunes`, `Trailer`,
    `Lo-Fi`, `Tribal`, `Acid Punk`, `Acid Jazz`, `Polka`, `Retro`,
    `Musical`, `Rock & Roll`, `Hard Rock`, `Folk`, `Folk-Rock`,
    `National Folk`, `Swing`, `Fast Fusion`, `Bebob`, `Latin`, `Revival`,
    `Celtic`, `Bluegrass`, `Avantgarde`, `Gothic Rock`, `Progressive Rock`,
    `Psychedelic Rock`, `Symphonic Rock`, `Slow Rock`, `Big Band`,
    `Chorus`, `Easy Listening`, `Acoustic`, `Humour`, `Speech`, `Chanson`,
    `Opera`, `Chamber Music`, `Sonata`, `Symphony`, `Booty Bass`, `Primus`,
    `Porn Groove`, `Satire`, `Slow Jam`, `Club`, `Tango`, `Samba`,
    `Folklore`, `Ballad`, `Power Ballad`, `Rhythmic Soul`, `Freestyle`,
    `Duet`, `Punk Rock`, `Drum Solo`, `Acapella`, `Euro-House`, `Dance Hall`,
    `Goa`, `Drum & Bass`, `Club-House`, `Hardcore`, `Terror`, `Indie`,
    `BritPop`, `Negerpunk`, `Polsk Punk`, `Beat`, `Christian Gangsta Rap`,
    `Heavy Metal`, `Black Metal`, `Crossover`, `Contemporary Christian`,
    `Christian Rock`, `Merengue`, `Salsa`, `Trash Metal`, `Anime`, `Jpop`,
    `Synthpop`
  );


var
  Form1: TForm1;

implementation

{$R *.dfm}

Procedure PrectiID3tag(z_jakeho_souboru: string; var ID3tag: TID3tag_typ);
Var
  fmp3: TFileStream;
begin
  fmp3:=TFileStream.Create(z_jakeho_souboru, fmOpenRead);
  try
    fmp3.position:=fmp3.size-128;
    fmp3.Read(ID3tag,SizeOf(ID3tag));
  finally
    fmp3.free;
  end;
end;


Procedure ZobrazID3tag(ID3: TID3tag_typ);
begin
if ID3.Hlavicka <> `TAG` then begin
   Form1.Edit1.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit2.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit3.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit4.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit5.Text:=`Neplatna nebo neexistujici ID3 informace`;
   Form1.Edit6.Text:=`Neplatna nebo neexistujici ID3 informace`;
 end else begin
   Form1.Edit1.Text:=ID3.nazev;
   Form1.Edit2.Text:=ID3.interpret;
   Form1.Edit3.Text:=ID3.album;
   Form1.Edit4.Text:=ID3.rok;
   Form1.Edit5.Text:=ID3.komentar;

   if ID3.Zanr in [0..CelkemZanru] then
     Form1.Edit6.Text:=ID3Zanr[ID3.Zanr]
   else
     Form1.Edit6.Text:=IntToStr(ID3.Zanr);
 end;
end;

 

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := `Prehravac MP3 :)`;
  MediaPlayer1.VisibleButtons := [btPlay, btPause, btStop];
  Button1.Caption := `Otevri`;

  Label1.Caption := `Nazev`;
  Label2.Caption := `Interpret`;
  Label3.Caption := `Album`;
  Label4.Caption := `Rok`;
  Label5.Caption := `Komentar`;
  Label6.Caption := `Zanr`;

  Edit1.Text := ``;
  Edit2.Text := ``;
  Edit3.Text := ``;
  Edit4.Text := ``;
  Edit5.Text := ``;
  Edit6.Text := ``;
end;

procedure TForm1.Button1Click(Sender: TObject);
var ID3tag: TID3Tag_typ;
begin
  if OpenDialog1.Execute then begin
    MediaPlayer1.Close;
    MediaPlayer1.FileName := OpenDialog1.FileName;
    PrectiID3tag(OpenDialog1.FileName, ID3tag);
    ZobrazID3tag(ID3tag);
    MediaPlayer1.Open;
  end;
end;

end.

Na závěr

Dnešní článek se zabýval ryze praktickými otázkami a ukázal, jakým způsobem obohatit přehrávač MP3 o možnost zobrazování tagů ID3. Věřím, že jste z teoretického popisu i z praktických ukázek pochopil, jak s ID3 tagy pracovat a že jste připraveni vytvářet případné vlastním, sofistikovanější aplikace pro správu těchto tagů.

Nutno ovšem podotknout, že vše, co dnešní díl ukázal, je velmi jednoduchým pohledem na celou problematiku. Pokud bychom chtěli vytvořit přehrávač, který by dokázal pracovat s ID3 tagy v jejich nejnovjší verzi, tegy s tagy ID3v2, byla by práce podstatně komplikovanější. ID3v2 tagy nemají pevně stanovené místo v souboru a především nemají pevně stanovenou délku, proto je práce s nimi poněkud obtížnější. Pro ilustraci celé problematiky však věřím, že dnešní aplikace postačovala.

Váš názor Další článek: Intel pracuje na snížení teploty svých procesorů

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,