在 Delphi 下使用 DirectSound (7): 播放资源文件中的 Wave 数据
在 Delphi 下使用 DirectSound (7): 播放资源文件中的 Wave 数据
涂孟超 发表于3年前
在 Delphi 下使用 DirectSound (7): 播放资源文件中的 Wave 数据
  • 发表于 3年前
  • 阅读 1
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云实验室 1小时搭建人工智能应用,让技术更容易入门 免费体验 >>>   


首先要修改前面自定义的 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
  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);
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.


本节演示录像: http://files.cnblogs.com/del/DirectSound_7.rar
共有 人打赏支持
粉丝 13
博文 2004
码字总数 14107
×
涂孟超
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: