Umíme to s Delphi: 135. díl – první MP3 Player v Delphi

Dnešní článek naváže na téma, které jsme otevřeli minule: ukátžeme si, jak snadno lze v Delphi vytvořit jednoduchý přehrávač souborů MP3. Zároveň si nastíníme několik problémů, které vytváření takové aplikace provází. Možná právě vy znáte jejich řešení: neváhejte a podělte se v diskusi.

V minulém dílu seriálu jsme se podrobně zabývali MP3 soubory, jejich podstatou a způsobem jejich vytváření. Ve stručnosti jsme si popsali základní principy MP3 komprese a vysvětlili jsme si, jak je možné, že MP3 soubory při srovnatelné kvalitě záznamu dokáží zabrat podstatně méně diskového prostoru než jiné druhy souborů, například soubory wav apod.

Před týdnem jsme se nedostali k jednomu ryze praktickému tématu, a to k tzv. ID3 tagům. Většina čtenářů asi ví, že ID3 tagy představují cestu, jak obohatit hudební (MP3) soubory o textové informace – díky tomu lze v empétrojce uchovávat nejen hudbu samotnou, ale také název skladby, jméno jejího autora a interpreta, název alba a řadu dalších údajů. K ID3 tagům se později vrátíme.

Abychom ale nežili pouze teorií, dnešní článek zaměříme ryze prakticky: vytvoříme společnými silami v Delphi svůj první přehrávač souborů MP3.

Jak na MP3 přehrávač v Delphi?

Nejprve se pokusme rozebrat, jaké máme k dispozici možnosti, chceme-li vytvořit přehrávač MP3 (jakýsi vlastní, i když nepochybně méně sofistikovaný WinAmp) v Delphi. V obecnosti se nabízejí tři různá řešení:

  • napsat jen jednoduchou aplikaci, která nebude sama nic přehrávat, ale bude (vzdáleně) ovládat jinou aplikaci, například WinAmp, která za nás samotné přehrávání vykoná. Tímto způsobem sice vytvoíme cosi, co bude ve výsledku fungovat dle očekávání, totiž bude to přehrávat empétrojky, ale o „přehrávači“ můžeme hovořit jen s hodně přimhouřenýma očima.
  • můžeme se pokusit sehnat, pořídit a nainstalovat specializovanou Delphi komponentu, která umožňuje přehrávání empétrojek. Tuto komponentu pak použijeme jako základ své aplikace. Komponent použitelných pro takový účel existuje celá řada, za zmínku stojí například komponenty XAudio (www.xaudio.com), které lze pro nekomerční použití pořídit bezplatně.
  • poslední možnost je ta, kterou společně realizujeme dnes: nikde nic neshánět, nikde nic nestahovat, nikde nic nepořizovat, vystačit si s prostředky Delphi a přesto napsat fungující přehrávač MP3. Úžasné, že?

Život může být jednodušší, než se zdá (alespoň někdy)

Někteří z vás o tom pravděpodobně vědí, pro ty ostatní odhalím hned v tuto chvíli podstatu celého zázraku: komponenta MediaPlayer, která je v Delphi standardně přítomna, umožňuje přehrávat soubory MP3. Přestože to na první pohled nemusí být patrné (MediaPlayer se podporou empétrojek nijak zvlášť nechlubí), je tomu tak a my si to společně vyzkoušíme.

Hned na začátek je nutné poznamenat, že k tomu, aby MediaPlayer dokázal empétrojky přehrát, je nutné mít v systému nainstalovaný MP3 kodek, nicméně předpokládám, že tento požadavek má drtivá většina čtenářů splněna.

Podívejme se tedy na komponentu MediaPlayer. Podrobně jsme se jí věnovali již před dávnými, dávnými časy, konkrétně v 19. dílu našeho seriálu. V Delphi ji (už od nepaměti) najdete v záložce System palety komponent. Umístíte-li ji na formulář, spatříte klasický ovládací panel umožňující základní operace s multimediálním souborem, viz následující obrázek:

Protože jsme se MediaPlayerem již zabývali, nebudeme znovu podrobně zkoumat jeho jednotlivé vlastnosti, metody a události. Zaměříme se jen na jednu jedinou vlastnost, a to na tu, která nás momentálně nejvíc zajímá: DeviceType. Tato vlastnost obsahuje typy multimédií, které mohou být otevřeny (tj. Přehrány) komponentou MediaPlayer.

Pokud se například v Object Inspectoru na tuto vlastnost podíváte, zjistíte, že žádná z předdefinovaných hodnot se ani vzdáleně nepodobá empétrojkám. V závislosti na používané verzi a edici Delphi bude Vaše množina použitelných hodnot vlastnosti DeviceType pravděpodobně podmnožinou (nebo nadmnožinou) následujících možností:

  • dtAutoSelect – typ multimédia je určen automaticky podle přípony příslušného souboru. Tato hodnota je přednastavená.
  • dtAVIVideo – videosoubor AVI
  • dtCDAudio – audio CD
  • dtDAT – DAT soubor
  • dtDigitalVideo Digital – digitální videozáznam
  • dtScanner – skener
  • dtSequencer – sekvencer
  • dtVCR – VCR analogový videozáznam
  • dtVideodisc – videodisc
  • dtWaveAudio – soubor s příponou *.wav
  • dtOther – multimédium je jiného druhu než kterékoliv z výše uvedených

Empétrojky veškeré žádné. Když si navíc zkusíte (napříkald v návrhové fázi aplikace) otevřít dialog pro výběr konkrétního multimediálního souboru (vlastnost FileName), žádné soubory MP3 se nezobrazí, protože je přednastavený filtr prosě neobsahuje. Standardn lze vybírat pouze ze souborů AVI, MID nebo WAV.

Všechny tyto premisy mohou vést k závěru, že MediaPlayer zkrátka a dobře empétrojky nepřehrává. Tento závěr je naštěstí chybný.

Vytváříme MP3 Player

Pojďme nyní společně vytvořit jednoduchou aplikaci, která demonstruje, že MediaPlayer opravdu dokáže přehrávat empétrojky. Založte v Delphi nový projekt a na formulář umístěte následující komponenty:

  • MediaPlayer ze záložky System,
  • OpenDialog ze záložky Standard,
  • Button ze záložky Standard.

Nyní ošetříme událost OnCreate hlavního formuláře. V její obsluze pozměníme některé vlastnosti formuláře a také komponenty MediaPlayer, protože budeme potřebovat jen omezenou množinu ovládacích tlačítek:

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

Po klepnutí na tlačítko Button1 dojde k zobrazení dialogu pro otevření souboru OpenDialog. V tomto dialogu bude uživatel moci vybrat (pro začátek jeden) soubor MP3 k přehrávání. Ošetříme tedy událost OnClick komponenty Buton1:

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

Věřte nevěřte, to je všechno. Tímto triviálním způsobm jsme vytvořili aplikaci, která bude schopna přehrávat soubory MP3. Vyzkoušejte, uvidíte.

Je samozřejmé, že aplikace prozatím trpí celou řadou much a chyb, nemá ošetřené žádné vstupy a je připravena otvírat (a následně chybově řvát) prakticky jakýkoliv typ souboru. Ve zbytku článku se tedy pokusíme program trochu vylepšit. Základní princip je ale jasný – MP3 přehrávač je na světě.

Drobná vylepšení – přehráváme jen a jen MP3

Nejprve ošetříme situaci, v níž se uživatel pokusí zvolit v dialogu OpenDialog takový soubor, jehož přípona je jiná než MP3. Pro začátek skutečně vytvoříme jen a pouze přehrávač MP3, takže pokud by uživatel zkusil zvolit jiný audiosoubor, byť by jej MediaPlayer s přehledem přehrál, vyhodíme chybové hlášení a nic nepřehrajeme.

Bude-li zlobivý uživatel chtít zvolit neplatný soubor, může to udělat dvěma způsoby:

  • najít jej v adresářové struktuře dialogu a poklepáním jej zvolit,
  • zadat jeho název ručně do příslušného políčkqa v dialogu.

Musíme zabránit oběma možnostem. První vyřešíme nastavením vlastností komponenty OpenDialog (např. v obsluze události OnCreate hlavního formuláře). Zajistíme, že se uživateli nezobrazí nic, co by nemělo příponu MP3:

  OpenDialog1.DefaultExt := `mp3`;
  OpenDialog1.Filter := `Soubory MP3 (*.mp3)|*.MP3`;

Uživatel však pořád ještě může požadovaný soubor zadat ručně. Poopravíme tedy obsluhu události OnClick tlačítka Button1:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    if ofExtensionDifferent in OpenDialog1.Options then begin
      ShowMessage(`Soubor s touto priponou nelze prehrat!`);
    end
    else begin
      MediaPlayer1.Close;
      MediaPlayer1.FileName := OpenDialog1.FileName;
      MediaPlayer1.Open;
    end;
  end;
end;

Jinak řečeno: pokud uživatel zadá soubor s jinou příponou než MP3 (ověříme prostřednictvím flagu ofExtensionDifferent), vypíšeme chybové hlášení a nic nepřehrajeme.

MediaPlayer není dokonalý...

Pokud si budete s vytvořenou aplikací chvíli hrát, všimnete si jistě celé řady dalších nedokonalostí.

Především – není pohodlné, když poté, co vyberete v dialogu OpenDialog soubor, musíte k jeho přehrání ještě znovu kliknout na zelené tlačítko Play na MediaPlayeru. Ideální by bylo, kdyby se soubor začal přehrávat automaticky ihned po jeho otevření.

Toho bychom mohli docílit relativně snadno – zavolat po otevření souboru metodu MediaPlayer.Play, viz ukázka:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    if ofExtensionDifferent in OpenDialog1.Options then begin
      ShowMessage(`Soubor s touto priponou nelze prehrat!`);
    end
    else begin
      MediaPlayer1.Close;
      MediaPlayer1.FileName := OpenDialog1.FileName;
      MediaPlayer1.Open;
      MediaPlayer1.Play;
    end;
  end;
end;

Pokud se to ale pokusíte učinit, hned zjistíte, že jsme jeden bug vykoupili jiným: soubor se sice začne automaticky přehrávat ihned po jeho otevření, avšak bohužel se neupdatují ovládací tlačítka na MediaPlayeru, a tak, pořád svítí zelené tlačítko Play, zatímco Pause a Stop jsou neaktivní, a to přesto, že přehrávání probíhá.

Dobře, řeknete si možná, update tlačítek přece nemůže být takový problém. Po chvilce pokusů však zjistíte, že je. Automatická aktualizace tlačítek MediaPlayeru prostě v tomto případě selhává. Řešením by bylo starat se o aktualizaci tlačítek ručně. K tomu bychom museli nastavit vlastnost AutoEnabled na False a následně po každé změně stavu MediaPlayeru (např. po dokončení přehrávání, po klepnutí na kterékoliv tlačítko, po otevřeí nového souboru apod.) ručně nastavit vlastnost EnabledButtons, což je seznam tlačítek, která jsou v daném okamžiku dostupná.

Abychom mohli toto ruční nastavování praktikovat, museli bychom nejprve nastavit vlastnost Notify na True, a to proto, aby po každé změně stavu MediaPlayeru docházelo ke generování události OnNotify. A konečně, v obsluze události OnNotify bychom museli otestovat aktuální hodnotu vlastnosti Mode, která obsahuje aktuální „stav“ komponenty MediaPlayer (tedy například mpStopped = zastaveno, mpPlaying = přehrávání apod.). Podle hodnoty vlastnosti Mode je pak nutné nastavit vlastnost EnabledButtons.

Teoreticky dává tento popis smysl, avšak přiznám se, že ani po několika provedených pokusech se mi nepodařilo převést teorii do praxe. Jinak řečeno, MediaPlayer se choval velmi záhadně a nedeterministicky: vlastnost Mode byla někdy nastavena dobře, jindy prostě ne. Ve výsledku se tlačítka občas překreslila dobře (např. po zastavení přehrávání se někdy správně zobrazilo tlačítko Play pro opětovné spuštění, jindy však zůstaly aktivní tlačítka Pause a Stop, jako by přehrávání stále probíhalo). Nepomohly ani experimenty s další zajímavou vlastnosti Wait, která určuje, zda má být řízení předáno aplikaci ihned po zavolání metody pro změnu stavu (tj. např. Play, Stop apod.) a nebo až po dokončení operace.

Pokud se někdo ze čtenářů MediaPlayerem podrobnji zabýval, uvítám, když nám předá své poznatky v diskusi. Prozatím nezbývá než konstatovat, že MediaPlayer se někdy nechová podle očekávání.

Zdrojový kód

Uvedené otazníky však nic nemění na zásadní podstatě celého dnešního článku: byť se to na první pohled nemusí zdát, komponenta MediaPlayer umí přehrávat soubory MP3, takže nenjí žádný problém vytvořit v Delphi fungující přehrávač empétrojek. Naše dnešní dílko je nesmírně jednoduché až primitivní, přeto dokáže přehrát (jednu) empétrojku. Následující zdrojový kód ukazuje proztatímní konečnou verzi aplikace:

unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := `Prehravac MP3 :)`;
  MediaPlayer1.VisibleButtons := [btPlay, btPause, btStop];
  Button1.Caption := `Otevri`;
  OpenDialog1.DefaultExt := `mp3`;
  OpenDialog1.Filter := `Soubory MP3 (*.mp3)|*.MP3`;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    if ofExtensionDifferent in OpenDialog1.Options then begin
      ShowMessage(`Soubor s touto priponou nelze prehrat!`);
    end
    else begin
      MediaPlayer1.Close;
      MediaPlayer1.FileName := OpenDialog1.FileName;
      MediaPlayer1.Open;
    end;
  end;
end;

end.

Na závěr

Dnes jsme si ukázali, jakým způsobem lze za použití standardních prostředků Delphi vytvořit jednoduchý, avšak fungující přehrávač souborů MP3. Zároveň jsme si nastínili některé problémy, které při použití MediaPlayer pozorujeme. Znovu si dovolím vyzvat všechny čtenáře, kteří se s tímto nebo jiným chováním MediaPlayeru také setkali, aby se o své zkušenosti podělili v diskusi.

V příštím dílu seriálu budeme dále vylepšovat dnešní aplikaci, ale především si něco povíme o velmi důležité součásti technologie MP3. Zaměříme se totiž na ID3 tagy, na jejich zpracování, na jejich extrakci z empétrojek, na jejich zobrazení v aplikaci a na jejich případnou modifikaci.

Diskuze (5) Další článek: Rozhodnutí o softwarových patentech opět odloženo

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