我在项目上用的一段代码, 不过只适用于格式相同的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 FHeaderdo
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 Headerdo
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, soFrombegin
ning);
FStream.Seek(0, soFrombegin
ning);
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, soFrombegin
ning);
FStream.Seek(0, soFrombegin
ning);
FStream.CopyFrom(aStream, aStream.Size);
FHeader := getPCMWaveHeader(FStream);
end;
function TPCMWave.getPCMWaveHeader(aStream: TStream): TPCMWaveHeader;
var
Header : TPCMWaveHeader;
begin
aStream.Seek(0, soFrombegin
ning);
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 FHeaderdo
begin
rLen := rLen + Header.dLen;
dLen := dLen + Header.dLen;
end;
//更新文件头
FStream.Seek(0, soFrombegin
ning);
FStream.Write(FHeader, SizeOf(FHeader));
//追加数据
FStream.Seek(0, soFromEnd);
aStream.Seek(SizeOf(TPCMWaveHeader), soFrombegin
ning);
//跳过文件头
FStream.CopyFrom(aStream, Header.dLen);
end;
end
else
//数据区为空, 读入新的数据
begin
LoadFromStream(aStream);
end;
end;
procedure TPCMWave.Clear;
begin
FillChar(FHeader, SizeOf(FHeader), 0);
with FHeaderdo
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.