Živě.cz o počítačích a internetu

Tipy a triky v Delphi, díl 56. - Soubory MP3

Jan Šindelář - 4.9.2002

Soubory MP3

Pro nezasvěcené (kdyby se někdo takový našel) by se možná slušel drobný úvod. Předpokládám, že dobře znáte soubory MP3. Pokud tyto soubory přehráváte, snad každý dnešní běžný přehrávač vám zobrazí tzv. ID3 tag. To jsou data, uložená na začátku či na konci MP3 souboru (záleží na verzi, viz. dále), která obsahují informace o interpretovi, albu, názvu skladby atd. Na samotné přehrávání hudby nemá tato část souboru vliv a přehrávač toto místo sám přeskočí. Ne každý soubor pochopitelně ID3 tag obsahuje, je to nepovinná součást.

Tolik tedy obecný úvod a pojďme nyní ke konkrétnějším věcem. Již jsem se zmínil o verzích. Základní hrubé rozdělení (nebudeme zabíhat do detailů) rozlišuje dosud dvě verze, verzi první a druhou. Ty se poté ještě dělí dále na menší subverze (obzvláště u verze 2), ale to pro nás dnes nebude podstatné. První verze tagu, která je pochopitelně starší a dodnes stále ještě nejpoužívanější, představuje záznam o velikosti pouhých 128 bajtů, který je uložen na samém konci souboru. Modernější verze 2, která začíná pomalu ale jistě dominovat, je naopak uložena na začátku souboru MP3, velikost již není pevně stanovena a ani struktura není direktivně určena. Obsahuje pochopitelně o mnoho více informací a práce s tímto tagem je (z programátorského hlediska) poněkud složitější.

Dnes se budeme pro začátek zabývat první verzí tagu a časem se možná dostane i na verzi 2, i když to by si již vyžádalo poněkud komplexnější vysvětlování. Ale uvidíme časem. Dnes tedy verze 1.

Jak již bylo řečeno, nachází se na konci souboru a má přesně 128 bajtů. Je pochopitelné, že do takového malého prostoru příliš informací neuložíme, ale něco přeci. Popišme si tedy blíže strukturu tohoto tagu. První tři bajty tvoří vždy slovo TAG a je to vlastně jeden ze znaků, podle kterého se dá testovat, zda soubor tag obsahuje nebo ne. Dalších 30 bajtů tvoří titulek skladby, pak následuje 30 bajtů pro název skupiny či umělce a dále rovněž 30 bajtů pro název alba. Následují 4 bajty pro rok vydání skladby, poté opět 30 bajtů - tentokrát pro komentář a poslední bajt obsahuje žánr skladby. Časem byl tento formát ještě drobně rozšířen (někdy pak bývá označován jako verze 1.1) o parametr čísla skladby. Velikost takového tagu je však stejná, pouze se drobně ukrojil prostor pro komentář. Platí pak pravidlo, že pokud je předposlední bajt komentáře nulový a poslední bajt naopak různý od nuly, vyjadřuje právě tento poslední bajt číslo skladby. Ještě je potřeba vysvětlit, jak je vyřešena otázka žánru. Na něj je vyhrazen pouhý jeden bajt, ale jak jistě dobře víte například při prohlížení detailních informací o souboru ve Winampu, názvy žánrů jsou pochopitelně slovní. Toho je docíleno celkem jednoduše tím, že je předem určena v normě ID3 množina definovaných žánrů se svými čísly. Každý program, který tedy s ID3 tagy pracuje, tuto tabulku v sobě obsahuje a ze souboru je pouze načten identifikující bajt.

Tolik tedy teorie a pokud byste chtěli o tomto tématu získat další znalosti, navštivte tuto adresu.

Nyní jsme tedy již teoreticky připraveni a můžeme se pustit do samotné implementace načítání tagů v Delphi. Věřím, že většina z vás by již tuto fázi zvládla bez problému sama, ale pro úplnost si uvedeme ukázkový kód. V něm si nejprve nadefinujeme strukturu záznamu pro ID3 tag a dále pak jednoduchou funkci pro načítání tagu ze souboru. Na formulář dále umístěte tlačítko, dialog pro otevření souboru a komponentu Memo pro výpis tagu. Samotné načítání umístíme do události OnClick tlačítka a zde bude zároveň i výpis.

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    OpenDialog1: TOpenDialog;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TagRecord = record
    Header: array [1..3] of Char;
    Title: array [1..30] of Char;
    Artist: array [1..30] of Char;
    Album: array [1..30] of Char;
    Year: array [1..4] of Char;
    Comment: array [1..30] of Char;
    Genre: Byte;
  end;


var
  Form1: TForm1;

implementation

{$R *.dfm}

function ReadTag(const FileName: string; var TagData: TagRecord): Boolean;
var
  SourceFile: file;
begin
  try
    Result := true;
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    Seek(SourceFile, FileSize(SourceFile) - 128);
    BlockRead(SourceFile, TagData, 128);
    CloseFile(SourceFile);
  except
    Result := false;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
Tag: TagRecord;
begin
  OpenDialog1.Execute;
  if OpenDialog1.FileName <> `` then ReadTag(OpenDialog1.FileName, Tag);
  if Tag.Header = `TAG` then
    begin
      Memo1.Clear;
      Memo1.Lines.Add(`Titulek: ` + TrimRight(Tag.Title));
      Memo1.Lines.Add(`Interpret: ` + TrimRight(Tag.Artist));
      Memo1.Lines.Add(`Album: ` + TrimRight(Tag.Album));
      Memo1.Lines.Add(`Rok: ` + TrimRight(Tag.Year));
      if ((Tag.Comment[29] = #0) and (Tag.Comment[30] <> #0)) or ((Tag.Comment[29] = #32) and (Tag.Comment[30] <> #32)) then
        begin
          Memo1.Lines.Add(`Komentář: ` + TrimRight(Copy(Tag.Comment, 1, 28)));
          Memo1.Lines.Add(`Číslo stopy: ` + IntToStr(Ord(Tag.Comment[30])));
        end
      else
        Memo1.Lines.Add(`Komentář: ` + TrimRight(Tag.Comment));
    end;
end;

end.

Pozornější z vás si jistě všimli, že jsem "takticky" vynechal položku žánr. Ten se samozřejmě během načítání rovněž ze souboru načte a uloží do záznamu Genre, ale v podobě čísla, které danému žánru odpovídá a jeho slovní vyjádření je nutné najít v příslušné tabulce (viz. úvodní text). Abych výsledný kód příliš nekomplikoval a neprodlužoval (žánrů je celkem 147), rozhodl jsem se tuto položku vynechat a opět vás musím odkázat na adresu, kde tyto hodnoty a jejich slovní vyjádření naleznete.

To bude pro dnešek vše a příště pravděpodobně ještě u MP3 souborů zůstaneme a povíme si něco o jeho vlastnostech, přesněji řečeno, jak tyto vlastnosti ze souborů získat.