谁能通过读Tstream播放声音?(40分)

  • 主题发起人 主题发起人 frogking
  • 开始时间 开始时间
F

frogking

Unregistered / Unconfirmed
GUEST, unregistred user!
注意:不是通过文件名!
 
要用底层API的。
http://go4.163.com/lovejingtao
 
http://www.banasoft.com/AVPhone.htm
 
Delphi支持直接从TStream里播放声音的:
MMSystem.PlaySound(FSoundStream.Memory, 0, SND_ASYNC or SND_MEMORY)
 
我的项目中的一段代码,参考一下吧

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

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;
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);
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);
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);
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.

 
后退
顶部