首页  编辑  

一个PCM Wav文件类

Tags: /超级猛料/Multi-Media.多媒体相关/   Date Created:

一个PCM Wav文件类

unit PCMWaves;

interface

uses

 Classes, SysUtils, MMSystem;

type

 TPCMWaveHeader = packed record

   rID             : array[0..3] of Char;   //'RIFF' 标识

   rLen            : Longint;

   wID             : array[0..3] of Char;   //'WAVE' 标识

   fID             : array[0..3] of Char;   //'fmt ' 标识

   fLen            : Longint;               //固定值, 16

   wFormatTag      : Word;                  //固定值, 1

   nChannels       : Word;                  //Mono=1, Stereo=2

   nSamplesPerSec  : Longint;               //采样频率, HZ

   nAvgBytesPerSec : Longint;

   nBlockAlign     : Word;

   nBitsPerSample  : Word;                  //精度, e.g. 8 or 16

   dId             : array[0..3]of Char;    //'data' 标识

   dLen            : Longint;               //数据长度

 end;

type

 EInvalidPCMWaveFormat = class(Exception);

 

 TPCMWave  = class(TObject)

 private

   FStream : TMemoryStream;

   FHeader : TPCMWaveHeader;

 protected

   function isValidFormat(aStream: TStream): Boolean; virtual;

   function getPCMWaveHeader(aStream: TStream): TPCMWaveHeader; virtual;

   function getData: Pointer; virtual;

   

 public

   constructor Create; virtual;

   destructor Destroy; override;

   procedure LoadFromFile(const aFileName: String); virtual;

   procedure LoadFromStream(aStream: TStream); virtual;

   procedure SaveToFile(const aFileName: String); virtual;

   procedure SaveToStream(aStream: TStream); virtual;

   procedure Append(const aFileName: String); overload; virtual;

   procedure Append(aStream: TStream); overload; virtual;

   procedure Clear;

   procedure Play;

   procedure Stop;

   

   property Header: TPCMWaveHeader read FHeader;

   property Data: Pointer read getData;

 end;

implementation

uses

 LogFiles;

 

constructor TPCMWave.Create;

begin

 FillChar(FHeader, SizeOf(FHeader), 0);

 with FHeader do

   begin

   rID             := 'RIFF';  //'RIFF' 标识

   rLen            := 36;      //dLen + 36

   wID             := 'WAVE';  //'WAVE' 标识

   fID             := 'fmt ';  //'fmt ' 标识

   fLen            := 16;      //固定值, 16

   wFormatTag      := 1;       //固定值, 1

   nChannels       := 1;       //Mono=1, Stereo=2

   nSamplesPerSec  := 16000;   //采样频率, HZ

   nAvgBytesPerSec := 32000;  //nSamplesPerSec*nChannels*Trunc(nBitsPerSample div 8);

   nBlockAlign     := 2;       //nChannels*(nBitsPerSample div 8);

   nBitsPerSample  := 16;      //精度, e.g. 8 or 16

   dId             := 'data';  //'data' 标识

   dLen            := 0;

   end;

 FStream := TMemoryStream.Create;

 Assert(FStream <> nil);

 

 FStream.Write(FHeader, SizeOf(FHeader));

end;

destructor TPCMWave.Destroy;

begin

 if Assigned(FStream) then FStream.Free;

 inherited Destroy;

end;

function TPCMWave.isValidFormat(aStream: TStream): Boolean;

var

 Header : TPCMWaveHeader;

begin

 Header := getPCMWaveHeader(aStream);

 with Header do

   begin

   Result := (rID = 'RIFF') and (wID = 'WAVE') and (wFormatTag = 1);

   end;

end;

procedure TPCMWave.SaveToFile(const aFileName: String);

var

 Stream: TStream;

begin

 Stream := TFileStream.Create(aFileName, fmCreate);

 Assert(Stream <> nil);

 try

   SaveToStream(Stream);

 finally

   Stream.Free;

 end;

end;

procedure TPCMWave.LoadFromFile(const aFileName: String);

var

 Stream: TStream;

begin

 Stream := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);

 Assert(Stream <> nil);

 try

   LoadFromStream(Stream);

 finally

   Stream.Free;

 end;

end;

procedure TPCMWave.SaveToStream(aStream: TStream);

begin

 aStream.Seek(0, soFromBeginning);

 FStream.Seek(0, soFromBeginning);

 aStream.CopyFrom(FStream, FStream.Size);

end;

procedure TPCMWave.LoadFromStream(aStream: TStream);

begin

 if not isValidFormat(aStream) then

   raise EInvalidPCMWaveFormat.Create('Invalid PCM Wave Format');

 aStream.Seek(0, soFromBeginning);

 FStream.Seek(0, soFromBeginning);

 FStream.CopyFrom(aStream, aStream.Size);

 FHeader := getPCMWaveHeader(FStream);

end;

function TPCMWave.getPCMWaveHeader(aStream: TStream): TPCMWaveHeader;

var

 Header : TPCMWaveHeader;

begin

 aStream.Seek(0, soFromBeginning);

 aStream.Read(Header, SizeOf(Header));

 Result := Header;

end;

function TPCMWave.getData: Pointer;

begin

 Result := FStream.Memory;

end;

procedure TPCMWave.Append(const aFileName: String);

var

 Stream: TStream;

begin

 Stream := TFileStream.Create(aFileName, fmOpenRead or fmShareDenyWrite);

 Assert(Stream <> nil);

 try

   Append(Stream);

 finally

   Stream.Free;

 end;

end;

procedure TPCMWave.Append(aStream: TStream);

var

 Header : TPCMWaveHeader;

 isSame : Boolean;

begin

 if not isValidFormat(aStream) then

   raise EInvalidPCMWaveFormat.Create('Invalid PCM Wave Format');

 //数据区非空, 追加

 if FHeader.dLen > 0 then

   begin

   //检测是否是相同的数据格式

   Header := getPCMWaveHeader(aStream);

   isSame := (Header.nChannels = FHeader.nChannels)             //

         and (Header.nSamplesPerSec = FHeader.nSamplesPerSec)   //

         and (Header.nBitsPerSample = FHeader.nBitsPerSample);  //

   //数据格式相同

   if isSame then

     begin

     //处理非标准格式, 按规范定义, rLen不包含RIFF标志和长度共8字节

     //因此,aStream.Size = SizeOf(TPCMWaveHeader)+Header.dLen

     //      aStream.Size = Header.rLen + 8

     //但在实际中发现存在rLen值为包含RIFF标志和长度8字节的文件

     if Header.dLen > aStream.Size-SizeOf(TPCMWaveHeader) then

       begin

       Header.dLen := aStream.Size - SizeOf(TPCMWaveHeader);

       end;

     //计算数据区长度

     with FHeader do

       begin

       rLen := rLen + Header.dLen;

       dLen := dLen + Header.dLen;

       end;

     //更新文件头

     FStream.Seek(0, soFromBeginning);

     FStream.Write(FHeader, SizeOf(FHeader));

     //追加数据

     FStream.Seek(0, soFromEnd);

     aStream.Seek(SizeOf(TPCMWaveHeader), soFromBeginning);   //跳过文件头

     FStream.CopyFrom(aStream, Header.dLen);

     end;

   end

 else //数据区为空, 读入新的数据

   begin

   LoadFromStream(aStream);

   end;

end;

procedure TPCMWave.Clear;

begin

 FillChar(FHeader, SizeOf(FHeader), 0);

 with FHeader do

   begin

   rID             := 'RIFF';  //'RIFF' 标识

   rLen            := 36;      //dLen + 36

   wID             := 'WAVE';  //'WAVE' 标识

   fID             := 'fmt ';  //'fmt ' 标识

   fLen            := 16;      //固定值, 16

   wFormatTag      := 1;       //固定值, 1

   nChannels       := 1;       //Mono=1, Stereo=2

   nSamplesPerSec  := 16000;   //采样频率, HZ

   nAvgBytesPerSec := 32000;  //nSamplesPerSec*nChannels*Trunc(nBitsPerSample div 8);

   nBlockAlign     := 2;       //nChannels*(nBitsPerSample div 8);

   nBitsPerSample  := 16;      //精度, e.g. 8 or 16

   dId             := 'data';  //'data' 标识

   dLen            := 0;

   end;

 FStream.Clear;

 FStream.Write(FHeader, SizeOf(FHeader));

end;

procedure TPCMWave.Play;

begin

 //声卡已安装

 if waveOutGetNumDevs > 0 then

   begin

   PlaySound(Data, hInstance, SND_MEMORY or SND_ASYNC);

   end;

end;

procedure TPCMWave.Stop;

begin

 //声卡已安装

 if waveOutGetNumDevs > 0 then

   begin

   PlaySound(nil, hInstance, 0);

   end;

end;

end.