2006. augusztus 2., szerda

Parse a wave file


Problem/Question/Abstract:

Access each chunk within a wave file is a tricky business but sometime you need to access the actual samples/data to get what you want...so how can that be done?

Answer:

A WAV file is binary file in the RIFF format, RIFF format enables the user to haev multiple information in the same file which can either be used or not.

The information is stored in chunks, each chunk have its type (4 chars) and side (dword) so it can be skipped if you are not interested in the data or to be read from the file.

You can download the demo software that shows wave file in a signal display graph with functions as: paning, zoom, multiple audio channels and more from

http://www.com-n-sense.com/ftproot/SignalDisplay.zip

(the zip file contains the wavefileparser component and signaldisplay component).

The following code parses WAV files into accessable chunks:

{*==============================================================================
          Copyright (C) 2002, All rights reserved, Com-N-Sense Ltd
================================================================================
File: WaveFileParser.pas
Author: Liran Shahar, Com-N-Sense Ltd
Updated: 24/03/2002
Purpose: Parsing wave file into chunks
================================================================================
  24/03/2002, Liran Shahar
  - Initial release.
==============================================================================*}
unit WaveFileParser;

interface

uses
  Sysutils, Classes;

type
  TChunkType = array[1..4] of char;

  PChunk = ^TChunk;
  TChunk = packed record
    cType: TChunkType;
    dwSize: cardinal;
    pData: pointer;
  end;

  TcnsWaveFileParser = class(TPersistent)
  private
    FFilename: AnsiString;
    Chunks: TList;
  protected
    procedure SetFilename(AFilename: AnsiString); virtual;
    function GetChunksCount: integer; virtual;
    function GetChunk(Index: integer): PChunk; virtual;
    procedure ProcessFile; virtual;
    procedure ClearChunks; virtual;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    function GetChunkByType(ChunkType: TChunkType): PChunk; virtual;
    property Filename: AnsiString read FFilename write SetFilename;
    property ChunksCount: integer read GetChunksCount;
    property Chunk[Index: integer]: PChunk read GetChunk;
  end;

implementation

const
  RIFF_SIGNATURE = 'RIFF';
  WAVE_SIGNATURE = 'WAVE';

type
  TRIFFHeader = packed record
    cSignature: TChunkType;
    dwSize: cardinal;
    cType: TChunkType;
  end;

constructor TcnsWaveFileParser.Create;
begin
  inherited Create;
  FFilename := '';
  Chunks := TList.Create;
end;

destructor TcnsWaveFileParser.Destroy;
begin
  ClearChunks;
  inherited Destroy;
end;

procedure TcnsWaveFileParser.SetFilename(AFilename: AnsiString);
begin
  if FFilename <> AFilename then
  begin
    ClearChunks;
    FFilename := AFilename;
    ProcessFile;
  end; // if
end;

function TcnsWaveFileParser.GetChunksCount: integer;
begin
  Result := Chunks.Count;
end;

function TcnsWaveFileParser.GetChunk(Index: integer): PChunk;
begin
  Result := nil;
  if (Index > -1) and (Index < Chunks.Count) then
    Result := Chunks[Index];
end;

procedure TcnsWaveFileParser.ProcessFile;
var
  WaveFile: TFileStream;
  Header: TRIFFHeader;
  Chunk: PChunk;
begin
  try
    WaveFile := TFileStream.Create(FFilename, fmOpenRead + fmShareDenyWrite);
    WaveFile.Read(Header, sizeof(Header));
    if (AnsiCompareText(Header.cSignature, RIFF_SIGNATURE) = 0) and
      (AnsiCompareText(Header.cType, WAVE_SIGNATURE) = 0) then
    begin
      while WaveFile.Position < WaveFile.Size do
      begin
        Chunk := AllocMem(sizeof(TChunk));
        with Chunk^ do
        begin
          WaveFile.Read(cType, sizeof(cType));
          WaveFile.Read(dwSize, sizeof(dwSize));
          pData := AllocMem(dwSize);
          WaveFile.Read(pData^, dwSize);
        end; // with
        Chunks.Add(Chunk);
      end; // while
    end; // if
  finally
    FreeAndNil(WaveFile);
  end;
end;

procedure TcnsWaveFileParser.ClearChunks;
var
  Chunk: PChunk;
begin
  while Chunks.Count > 0 do
  begin
    Chunk := Chunks[0];
    Chunks.Delete(0);
    if assigned(Chunk^.pData) then
      FreeMem(Chunk^.pData);
    dispose(Chunk);
  end; // while
end;

function TcnsWaveFileParser.GetChunkByType(ChunkType: TChunkType): PChunk;
var
  iIndex: integer;
begin
  Result := nil;
  iIndex := 0;
  while iIndex < Chunks.Count do
    if AnsiCompareText(PChunk(Chunks[iIndex])^.cType, ChunkType) = 0 then
    begin
      Result := Chunks[iIndex];
      break;
    end
    else
      iIndex := iIndex + 1;
end;

end.


Component Download: http://www.com-n-sense.com/ftproot/SignalDisplay.zip

Nincsenek megjegyzések:

Megjegyzés küldése