Thursday, May 22, 2008

Pro výpis informací o souboru použijeme podobně jako minule opět komponentu Memo, takže i tu je třeba umístit na formulář.
Samotné zpracování načtených informací a jejich zobrazení budeme provádět v události OnClick komponenty FileListBox.
procedure TForm1.FileListBox1Click(Sender: TObject);
var
soubor: MPEG_Header;
f: file;
hlavicka: header_type;

begin
try
Assignfile(f, Filelistbox1.Filename);
Reset(f, 1);
blockread(f, hlavicka, SizeOf(hlavicka));
if not (hlavicka[0] <> 255) or ((hlavicka[1] or $1F) <> 255) then
begin
if ((hlavicka[1] and 16) shr 4) = 0 then soubor.version := 2
else soubor.version := (hlavicka[1] and 8) shr 3;
soubor.layer := 4 - ((hlavicka[1] and 6) shr 1);
soubor.protect := hlavicka[1] and 1;
soubor.bitrate := (hlavicka[2] and 240) shr 4;
soubor.samplerate := (hlavicka[2] and 12) shr 2;
soubor.padding := (hlavicka[2] and 2) shr 1;
soubor.extension := (hlavicka[2] and 1);
soubor.channelmode := (hlavicka[3] and 192) shr 6;
soubor.modeextension := (hlavicka[3] and 48) shr 4;
soubor.copyright := (hlavicka[3] and 8) shr 3;
soubor.original := (hlavicka[3] and 4) shr 2;
soubor.emphasis := (hlavicka[3] and 3);

Memo1.Clear;
Memo1.Lines.Add('MPEG ' + MPEG_version[soubor.version] + ' Layer ' + MPEG_layer[soubor.layer]);
Memo1.Lines.Add(IntToStr(MPEG_bitrates[soubor.version, soubor.layer-1, soubor.bitrate]) + ' kbps');
Memo1.Lines.Add(IntToStr(MPEG_frequencies[soubor.version, soubor.samplerate]) + ' Hz');
Memo1.Lines.Add(MPEG_channel[soubor.channelmode]);
Memo1.Lines.Add('Private : ' + MPEG_extension[soubor.extension]);
Memo1.Lines.Add('CRCs : ' + MPEG_protection[soubor.protect]);
Memo1.Lines.Add('Copyrighted : ' + MPEG_copyright[soubor.copyright]);
Memo1.Lines.Add('Original : ' + MPEG_original[soubor.original]);
Memo1.Lines.Add('Emphasis : ' + MPEG_emphasis[soubor.emphasis]);
end
else Memo1.Lines.Add('Chyba !!!!!');
except
end;
end;
Toto je tedy zhruba základ toho, co lze ze souboru MPEG vyčíst a co je asi pro běžného uživatele nejdůležitější. Pokud by vás zajímaly další informace, které se dají z MPEG souboru "vyždímat", odkáži vás pouze na internet jako zdroj informací. Nebudu vás směrovat na konkrétní stránku, aby to nedopadlo jako minule, ale tentokrát již třeba v době čtení tohoto článku bude www.id3.org fungovat, takže to zkuste například tam.

Monday, May 19, 2008

Tipy a triky v Delphi

Minule jsem si slíbili, že si budeme povídat o souborech MPEG a jak získat z těchto souborů rozličné informace. Ukážeme si tedy, jak zjistit například vzorkovací frekvenci či datový tok a další základní informace.
Podobně jako v minulém díle, vytvoříme si nejprve základní struktury, které budou soubor MPEG (či přesněji jeho hlavičku) popisovat. Na rozdíl od minule, kdy jsme se zabývali ID3 popiskem a informace jsme prakticky rovnou načítali ze souboru, musíme si v tomto případě nejprve nadefinovat řadu konstant. Ty budou obsahovat jednak příslušné frekvence, datové toky a další informace, které jsou standardní a předem definované a každý soubor se dá zařadit do jedné z kolonek. Tyto konstanty tedy mohou vypadat například takto:
const
MPEG_version: array[0..2] of string = ('2', '1', '2.5');
MPEG_layer: array[0..3] of string = ('neznámý', 'I', 'II', 'III');
MPEG_protection: array[0..1] of string = ('Ano', 'Ne');
MPEG_frequencies: array[0..2, 0..3] of word = ((22050,24000,16000,0), (44100,48000,32000,0), (11025,12000,8000,0));
MPEG_bitrates: array[0..2,0..2,0..15] of word=
(((0,32,48,46,64,80,96,112,128,144,160,176,192,224,256,0),
(0, 8,46,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0),
(0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0)),

((0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
(0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0),
(0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0)),

((0,32,48,46,64,80,96,112,128,144,160,176,192,224,256,0),
(0, 8,46,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0),
(0, 8,16,24,32,40,48, 56, 64, 80, 96,112,128,144,160,0)));

MPEG_padding: array[0..1] of string = ('All bits in frame are used', 'Unused bits are filled');

MPEG_extension: array[0..1] of string = ('Ne','Ano');

MPEG_channel: array[0..3] of string = ('Stereo', 'Joint stereo', 'Dual channel', 'Mono');

MPEG_modeext: array[0..3,0..3] of word =
((4,8,12,16),
(4,8,12,16),
(0,4,8,16),
(0,0,0,0));

MPEG_copyright: array[0..1] of string = ('Ne','Ano');

MPEG_original: array[0..1] of string = ('Ne','Ano');

MPEG_emphasis: array[0..3] of string = ('None','50/15 microseconds','Unknown','CITT j.17');
Nyní je třeba si nadefinovat strukturu pro vlastní hlavičku MPEG souboru a její typ, kterou lze popsat kupříkladu takovýmto záznamem:
MPEG_header = record
version,
layer,
protect,
bitrate,
samplerate,
padding,
extension,
channelmode,
modeextension,
copyright,
original,
emphasis:byte;
end;

header_type = array[0..3] of byte;
A teď již zbývá jen informace z jednotlivých souborů MP3 načíst do předem připravených struktur, zařadit je podle definovaných konstant do příslušné kategorie, zpracovat a výsledek zobrazit. Abychom si náš příklad usnadnili, použijeme pro celé rozhraní procházení disků, složek a souborů komponenty ze záložky Win 3.1, konkrétně tedy komponenty DriveComboBox, DirectoryListBox a FileListBox. Ty navzájem "propojíme" v Object Inspectoru jejich vlastnostmi tak, jak je běžně zvykem, tedy aby se změna v jedné komponentě interaktivně projevila i v komponentách ostatních.

Thursday, May 15, 2008

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.

Friday, May 02, 2008

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.