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

Tipy a triky v Delphi, díl 54. - Soubory "WAVE"

Jan Šindelář - 21.8.2002

Soubory "WAVE"

Soubory WAVE jsou alespoň na platformě Windows oblíbeným a celkem dlouhá léta standardním formátem pro ukládání zvukových dat. Hudbu máte sice pravděpodobně uloženu v jiném formátu kvůli úspoře místa, ale například pro nahrávání zvuků (hudby) z nějakého externího zdroje a následnou editaci je to celkem použitelný formát, který zvládá snad každý zvukový editor a můžete tak bez ztráty kvality zvuky upravovat. Až poté je teprve převedete do nějakého úspornějšího formátu (MP3, WMA a podobně). Rovněž rozličné systémové zvuky či zvuky v aplikacích jsou uloženy v tomto formátu. Proto jistě nebude na škodu, když si teď ukážeme, jak získat z hlavičky souboru některé zajímavé údaje a vlastnosti souboru.

Nejprve si vytvoříme datovou strukturu, do které tyto informace z hlavičky načteme. Můžeme k tomu využít například záznam (record) a ten bude vypadat takto:

.
.
type
  WAVrecord = record
    RIFFHeader: array [1..4] of Char;
    FileSize: Integer;
    WAVEHeader: array [1..4] of Char;
    FormatHeader: array [1..4] of Char;
    FormatSize: Integer;
    FormatID: Word;
    ChannelNumber: Word;
    SampleRate: Integer;
    BytesPerSecond: Integer;
    BlockAlign: Word;
    BitsPerSample: Word;
    DataHeader: array [1..4] of Char;
    SampleNumber: Integer;
  end;
.
.

Nyní můžeme začít vytvářet naší ukázkovou aplikaci. Na prázdný formulář tedy nejprve umístíme tři komponenty, které se nám postarají o procházení adresáři a disky. Kvůli zjednodušení můžeme použít například komponenty FileListBox, DirectoryListBox a DriveComboBox ze záložky Win 3.1 na paletě komponent. Komponenty pak navzájem "propojíme" běžným způsobem nastavením vlastností v Object Inspectoru tak, aby se změna disku či složky automaticky projevila i na výpis souborů a souborovou masku omezíme na soubory WAV.

Pro výpis informací, získaných ze souborů, můžeme použít například Label, respektive jejich větší množství (podle toho kolik a jaké informace budeme vypisovat), ale použít můžete pochopitelně cokoliv, to již nechám na vás. Samotné načtení a zobrazení informací pak umístíme do události OnChange komponenty FileListBox.

A to už se tedy postupně dostáváme k samotnému výpisu informací. Nejdříve si ještě nadefinujeme globální proměnnou, do které budou data načítána a poté již následuje vlastní funkce pro čtení dat ze souboru:

.
.
var
  soubor: WAVrecord;
.
.


function ReadWAV(const FileName: string; var WAVData: WAVrecord): Boolean;
var
  SourceFile: file;
begin
  try
    Result := true;
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    BlockRead(SourceFile, WAVData, 40);
    if WAVData.DataHeader <> `data` then
    begin
      Seek(SourceFile, WAVData.FormatSize + 28);
      BlockRead(SourceFile, WAVData.SampleNumber, 4);
    end;
    CloseFile(SourceFile);
  except
    Result := false;
  end;
end;

Ještě než se dostaneme k samotnému zpracování načtených dat a jejich zobrazení, vytvoříme si jednoduchou funkci, která nám otestuje hlavičku souboru a zjistí, zda je platná či ne a jestli má tím pádem smysl, pokoušet se vypisovat nějaké informace. Funkci pak poté využijeme v další části kódu.

function HeaderOK(const WAVData: WAVRecord): Boolean;
begin
  Result := true;
  if WAVData.RIFFHeader <> `RIFF` then Result := false;
  if WAVData.WAVEHeader <> `WAVE` then Result := false;
  if WAVData.FormatHeader <> `fmt ` then Result := false;
  if (WAVData.ChannelNumber <> 1) and (WAVData.ChannelNumber <> 2) then Result := false;
end;

A teď už se konečně dostáváme k výpisu informací na obrazovku. Jak jsme si řekli na začátku, výpis bude součástí události OnChange komponenty FileListBox. Když se podíváte na strukturu načítaných dat na začátek článku a porovnáte s těmi daty, které vypisujeme, jistě vidíte, že jsme nepoužili všechny možnosti, ale nechtěl jsem ukázku příliš prodlužovat. Myslím, že struktura záznamu je svými názvy dostatečně popsána a tak pro vás nebude problém vypsat i další informace. Proto jsou v našem příkladu převážně ty údaje, které jsou vlastně konstantami (a asi by se slušelo je jako konstanty nadefinovat, tak promiňte to zjednodušení). Vše ostatní již z názvů v záznamu pochopíte.

procedure TForm1.FileListBox1Change(Sender: TObject);
begin
Label1.Caption := ``;
Label2.Caption := ``;
Label3.Caption := ``;
Label4.Caption := ``;
Label5.Caption := ``;
if FileListBox1.FileName <> `` then
begin
ReadWAV(FileListBox1.FileName, soubor);
if HeaderOK(soubor) then
                    begin
                      Label1.Caption := `Hlavička souboru v pořádku - soubor rozpoznán`;
                      case Soubor.FormatID of
                      1: Label2.Caption := `Windows PCM`;
                      2: Label2.Caption := `Microsoft ADPCM`;
                      6: Label2.Caption := `A-LAW`;
                      7: Label2.Caption := `MU-LAW`;
                      17: Label2.Caption := `DVI/IMA ADPCM`;
                      85: Label2.Caption := `MPEG Layer III`;
                      else
                      Label2.Caption := `Neznámý formát`;
                      end;

                      case Soubor.ChannelNumber of
                      0: Label3.Caption := `Neznámý typ`;
                      1: Label3.Caption := `Mono`;
                      2: Label3.Caption := `Stereo`;
                      end;

                      Label4.Caption := IntToStr(Soubor.BitsPerSample) + ` bit`;
                      Label5.Caption := IntToStr(Soubor.SampleRate) + ` Hz`;
                    end
                      else
                      Label1.Caption := `Hlavička souboru není v pořádku - soubor nerozpoznán`;
end;
end;

A to je pro dnešek vše. V některém z příštích dílu se určitě také podíváme na další zvukové formáty jako třeba MP3. Tento díl je zároveň první, ve kterém jsem se pokusil na základě reakcí některých čtenářů zlepšit čitelnost kódu zvýrazněním klíčových slov. Pokud vaše ohlasy budou kladné, začneme s tím od příště natrvalo.