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
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése