首先要修改前面自定義的 ReadWaveFile 單元, 給它增加一個 OpenResource() 方法以直接讀取資源文件中的 "WAVE" 數據;
為避免混淆, 把單元名 ReadWaveFile 同時改為 ReadWave; 類名 TReadWaveFile 改為 TReadWave.
{修改後的 ReadWave 單元: 從文件或資源讀取 Wave 的格式、數據與數據尺寸}
unit ReadWave;
interface
uses Windows, Classes, SysUtils, MMSystem;
type
TReadWave = class
private
FFileHandle: HMMIO;
FFormat: TWaveFormatEx;
FSize: DWord;
function GetFormatAndSize(hFile: HMMIO): Boolean;
public
destructor Destroy; override;
function Open(FileName: string): Boolean;
function OpenResource(ResName: string): Boolean;
function Read(pDest: Pointer; Size: DWord): Boolean; //讀出波形數據
property Format: TWaveFormatEx read FFormat; //讀出格式數據
property Size: DWord read FSize; //讀出波形數據的大小
end;
implementation
{ TReadWave }
destructor TReadWave.Destroy;
begin
if FFileHandle > 0 then mmioClose(FFileHandle, 0);
inherited;
end;
function TReadWave.GetFormatAndSize(hFile: HMMIO): Boolean;
var
ckiRIFF,ckiFmt,ckiData: TMMCKInfo;
begin
Result := False;
if hFile = 0 then Exit;
ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo));
mmioDescend(hFile, @ckiRIFF, nil, MMIO_FINDRIFF);
if (ckiRIFF.ckid <> FOURCC_RIFF) or (ckiRIFF.fccType <> mmiOStringToFOURCC('WAVE',0)) then Exit;
ZeroMemory(@FFormat, SizeOf(TWaveFormatEx));
ZeroMemory(@ckiFmt, SizeOf(TMMCKInfo));
ckiFmt.ckid := mmiOStringToFOURCC('fmt', 0);
ZeroMemory(@ckiData, SizeOf(TMMCKInfo));
ckiData.ckid := mmiOStringToFOURCC('data', 0);
if (mmioDescend(hFile, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then mmioRead(hFile, @FFormat, SizeOf(TWaveFormatEx));
mmioAscend(hFile, @ckiFmt, 0);
if (mmioDescend(hFile, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then FSize := ckiData.cksize;
Result := FFormat.wFormatTag = WAVE_FORMAT_PCM;
end;
function TReadWave.Open(FileName: string): Boolean;
begin
Result := False;
if not FileExists(FileName) then Exit;
if FFileHandle > 0 then mmioClose(FFileHandle, 0);
FFileHandle := mmioOpen(PChar(FileName), nil, MMIO_READ);
Result := GetFormatAndSize(FFileHandle);
end;
function TReadWave.OpenResource(ResName: string): Boolean;
var
res: TResourceStream;
mmioInfo: TMMIOInfo;
begin
Result := False;
res := TResourceStream.Create(HInstance, ResName, 'WAVE');
ZeroMemory(@mmioInfo, SizeOf(TMMIOInfo));
mmioInfo.fccIOProc := FOURCC_MEM;
mmioInfo.cchBuffer := res.Size;
mmioInfo.pchBuffer := res.Memory;
if FFileHandle > 0 then mmioClose(FFileHandle, 0);
FFileHandle := mmioOpen(nil, @mmioInfo, MMIO_ALLOCBUF or MMIO_READ);
Result := GetFormatAndSize(FFileHandle);
res.Free;
end;
function TReadWave.Read(pDest: Pointer; Size: DWord): Boolean;
begin
Result := mmioRead(FFileHandle, pDest, Size) = Size;
end;
end.
下面的例子如圖載入了三個 Wave 文件到資源:
本例可充分體現 DirectSound 可同時播放多個聲音的特點; 實現代碼:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton; //播放第一個資源
Button2: TButton; //播放第二個資源
Button3: TButton; //播放第三個資源
Button4: TButton; //全部停止
CheckBox1: TCheckBox; //控制是否循環播放
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses DirectSound, MMSystem, ReadWave; //ReadWave 是上面重新定義的單元
var
myDSound: IDirectSound8;
bufs: array[0..2] of IDirectSoundBuffer; //緩沖區數組, 用於裝載資源文件中的三個 WAVE 文件
{使用資源文件建立緩沖區並播放}
procedure PlayResourceWave(ResName: string; var buf: IDirectSoundBuffer; loop: Boolean=false);
var
wavFormat: TWaveFormatEx;
bufDesc: TDSBufferDesc;
wav: TReadWave;
p1: Pointer;
n1: DWord;
begin
buf := nil;
wav := TReadWave.Create;
if not wav.OpenResource(ResName) then
begin
ShowMessage('打開失敗');
wav.Free;
Exit;
end;
ZeroMemory(@bufDesc, SizeOf(TDSBufferDesc));
bufDesc.dwSize := SizeOf(TDSBufferDesc);
bufDesc.dwFlags := DSBCAPS_STATIC;
bufDesc.dwBufferBytes := wav.Size;
bufDesc.lpwfxFormat := @wav.Format;
myDSound.CreateSoundBuffer(bufDesc, buf, nil);
buf.Lock(0, 0, @p1, @n1, nil, nil, DSBLOCK_ENTIREBUFFER);
wav.Read(p1, n1);
buf.Unlock(p1, n1, nil, 0);
if loop then buf.Play(0, 0, DSBPLAY_LOOPING) else buf.Play(0, 0, 0);
wav.Free;
end;
{初始化設備}
procedure TForm1.FormCreate(Sender: TObject);
var
wavFormat: TWaveFormatEx;
bufDesc: TDSBufferDesc;
begin
DirectSoundCreate8(nil, myDSound, nil);
myDSound.SetCoOperativeLevel(Handle, DSSCL_NORMAL);
end;
{播放第一個資源}
procedure TForm1.Button1Click(Sender: TObject);
begin
PlayResourceWave('wav_1', bufs[0], CheckBox1.Checked);
end;
{播放第二個資源}
procedure TForm1.Button2Click(Sender: TObject);
begin
PlayResourceWave('wav_2', bufs[1], CheckBox1.Checked);
end;
{播放第三個資源}
procedure TForm1.Button3Click(Sender: TObject);
begin
PlayResourceWave('wav_3', bufs[2], CheckBox1.Checked);
end;
{全部停止}
procedure TForm1.Button4Click(Sender: TObject);
var
i: Integer;
begin
for i := Low(bufs) to High(bufs) do
if bufs[i] <> nil then bufs[i].Stop;
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
i: Integer;
begin
for i := Low(bufs) to High(bufs) do bufs[i] := nil;
myDSound := nil;
end;
end.