在 Delphi 下使用 DirectSound (9): 效果器初步及 IDirectSoundFXGargle8 效果器


只有使用 IDirectSoundBuffer8 的次缓冲区才能设置"特效", 主缓冲区主要负责的是混音和处理 3D 效果.

IDirectSoundBuffer8(非 IDirectSoundBuffer) 支持以下效果器:

IDirectSoundFXChorus8      //合唱; 微调原生与回声的延迟

IDirectSoundFXCompressor8  //压缩; 压缩某些振幅

IDirectSoundFXDistortion8  //失真; 将波形顶部修改为方形或锯齿形

IDirectSoundFXEcho8        //回声; 重复并衰减回声

IDirectSoundFXFlanger8     //镶边; 延迟回声

IDirectSoundFXGargle8      //漱口; 有人叫它咕嘟效果

IDirectSoundFXI3DL2Reverb8 //环境混响; 房间、大厅等

IDirectSoundFXParamEq8     //均衡; 缩放不同频率的信号

IDirectSoundFXWavesReverb8 //混响; DirectX Media Objects(DMOs), 是微软从 Waves 购买的声音处理技术 



使用步骤:
1、通过 IDirectSoundBuffer8 的 SetFX() 方法关联特效, 这个过程主要是给 SetFX() 方法的参数准备 TDSEffectDesc 结构数组;
2、通过 IDirectSoundBuffer8 的 GetObjectInPath() 方法获取特效对象;
3、通过特效对象的 SetAllParameters() 方法设置特效参数.

9 个特效对象都只有两个方法: GetAllParameters()、SetAllParameters(), 两方法的参数都是结构体(各不相同).


{给 IDirectSoundBuffer8 关联特效; 不能在缓冲区锁定或播放时使用:}

function SetFX(

  dwEffectsCount: DWORD;    //特效数目, 即第二个参数 pDSFXDesc 的大小

  pDSFXDesc: PDSEffectDesc; //TDSEffectDesc 结构的数组

  pdwResultCodes: PDWORD    //接收设置结果, 不需要则给 nil

): HResult; stdcall;



//可以使用 SefFX(0, nil, nil) 删除缓冲区的所有特效.



{SetFX() 方法用到的结构体:}

TDSEffectDesc = packed record

  dwSize        : DWORD;     //结构大小

  dwFlags       : DWORD;     //处理标志; 一般给 0

  guidDSFXClass : TGUID;     //指定要使用的效果

  dwReserved1   : DWORD_PTR; //未使用

  dwReserved2   : DWORD_PTR; //未使用

end;



//DSEffectDesc.dwFlags

DSFX_LOCHARDWARE = $00000001; //使用硬件处理效果; 这其实在 Direct9.0 还不支持

DSFX_LOCSOFTWARE = $00000002; //使用软件处理效果; 同默认

0                             //默认



//DSEffectDesc.guidDSFXClass, 这分别是上面九个接口对应的 GUID 查询标识:

GUID_DSFX_STANDARD_CHORUS 

GUID_DSFX_STANDARD_COMPRESSOR 

GUID_DSFX_STANDARD_DISTORTION 

GUID_DSFX_STANDARD_ECHO 

GUID_DSFX_STANDARD_FLANGER 

GUID_DSFX_STANDARD_GARGLE 

GUID_DSFX_STANDARD_I3DL2REVERB 

GUID_DSFX_STANDARD_PARAMEQ 

GUID_DSFX_WAVES_REVERB 





{从 IDirectSoundBuffer8 获取特效对象的方法}

function GetObjectInPath(

  const rguidObject: TGUID;    //对象查询标识, GUID_DSFX_ ...

  dwIndex: DWORD;              //该特效在 SetFX() 安排特效数组时的索引 

  const rguidInterface: TGUID; //对象的唯一标识, IID_IDirectSoundFX ... 或 IDirectSoundFX ...

  out ppObject                 //返回要获取的特效接口

): HResult; stdcall;



{IDirectSoundFXGargle8.SetAllParameters() 需要结构体}

TDSFXGargle = packed record

  dwRateHz: DWORD;    //频率; 取值范围 1..1000, 默认 20

  dwWaveShape: DWORD; //波形; 三角波(0)、方波(1)

end;



//TDSFXGargle.dwRateHz 取值范围, 默认是 20

DSFXGARGLE_RATEHZ_MIN    = 1;    //

DSFXGARGLE_RATEHZ_MAX    = 1000; //



DSFXGARGLE_WAVE_TRIANGLE = 0; //三角波

DSFXGARGLE_WAVE_SQUARE   = 1; //方波



为让代码更简洁, 又把前面自定义的 ReadWave 该为了 ReadWave2 (增加了一个 OpenDialog 方法):

unit ReadWave2;



interface



uses Windows, Classes, SysUtils, MMSystem, Dialogs;



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;     //从资源打开, 资源的指定格式必须是 WAVE

  function OpenDialog: 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.OpenDialog: Boolean;

begin

  with TOpenDialog.Create(nil) do begin

    Filter := 'Wave File(*.wav)|*.wav';

    if Execute then Result := Open(FileName);

    Free;

  end;

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.



测试代码:

unit Unit1;



interface



uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs, StdCtrls, ExtCtrls, ComCtrls;



type

  TForm1 = class(TForm)

    Button1: TButton;         //打开并播放

    Button2: TButton;         //停止

    TrackBar1: TTrackBar;     //用于调整 TDSFXGargle.dwRateHz

    RadioGroup1: TRadioGroup; //用于调整 TDSFXGargle.dwWaveShape

    procedure FormCreate(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

    procedure TrackBar1Change(Sender: TObject); //RadioGroup1.OnClick 也关联它

  end;



var

  Form1: TForm1;



implementation



{$R *.dfm}



uses DirectSound, ReadWave2; //ReadWave2 是刚刚修改过的自定义单元



var

  myDSound: IDirectSound8;

  buf8: IDirectSoundBuffer8;

  fxGargle: IDirectSoundFXGargle8; //IDirectSoundFXGargle8 效果器



{建立设备对象并初始化界面}

procedure TForm1.FormCreate(Sender: TObject);

begin

  DirectSoundCreate8(nil, myDSound, nil);

  myDSound.SetCooperativeLevel(Handle, DSSCL_NORMAL);



  TrackBar1.Min := DSFXGARGLE_RATEHZ_MIN; //1

  TrackBar1.Max := DSFXGARGLE_RATEHZ_MAX; //1000

  TrackBar1.Position := 20;               //默认值

  TrackBar1.ShowSelRange := False;

  TrackBar1.TickStyle := tsNone;



  RadioGroup1.Items.CommaText := 'TRIANGLE, SQUARE';

  RadioGroup1.Columns := 2;

  RadioGroup1.ItemIndex := 0;

  RadioGroup1.OnClick := TrackBar1.OnChange; //两个事件的代码相同



  System.ReportMemoryLeaksOnShutdown := true;

end;



{建立缓冲区、关联特效、获取特效对象并播放}

procedure TForm1.Button1Click(Sender: TObject);

var

  buf: IDirectSoundBuffer; //最终需要的是 IDirectSoundBuffer8, 这里的 IDirectSoundBuffer 只是做个桥

  bufDesc: TDSBufferDesc;

  rEffect: TDSEffectDesc;  //SetFX() 方法需要的结构

  wav: TReadWave;

  p1: Pointer;

  n1: DWORD;

begin

  {经过对自定义单元的修改, 现在调入一个 Wave 很方便}

  wav := TReadWave.Create;

  if not wav.OpenDialog then begin wav.Free; Exit; end;



  {获取 IDirectSoundBuffer8 接口对象}

  ZeroMemory(@bufDesc, SizeOf(TDSBufferDesc));

  bufDesc.dwSize := SizeOf(TDSBufferDesc);

  bufDesc.dwFlags := DSBCAPS_CTRLFX; //!

  bufDesc.dwBufferBytes := wav.Size;

  bufDesc.lpwfxFormat := @wav.Format;

  myDSound.CreateSoundBuffer(bufDesc, buf, nil);

  buf.QueryInterface(IID_IDirectSoundBuffer8, buf8);



  {载入波形}

  buf8.Lock(0, 0, @p1, @n1, nil, nil, DSBLOCK_ENTIREBUFFER);

  wav.Read(p1, n1);

  wav.Free;

  buf8.Unlock(p1, n1, nil, 0);



  {准备 SetFX() 需要的结构}

  ZeroMemory(@rEffect, SizeOf(TDSEffectDesc));

  rEffect.dwSize := SizeOf(TDSEffectDesc);

  rEffect.dwFlags := 0;

  rEffect.guidDSFXClass := GUID_DSFX_STANDARD_GARGLE; //指定是 IDirectSoundFXGargle8 效果器



  {关联效果器}

  buf8.SetFX(1, @rEffect, nil); //参数应该是个数组, 既然只有一个元素, 就先省了



  {获取效果器对象}

  buf8.GetObjectInPath(GUID_DSFX_STANDARD_GARGLE, 0, IID_IDirectSoundFXGargle8, fxGargle);



  {播放}

  buf8.Play(0, 0, DSBPLAY_LOOPING);



  //  buf := nil; //局部接口会被自动释放

end;



{停止播放}

procedure TForm1.Button2Click(Sender: TObject);

begin

  if Assigned(buf8) then buf8.Stop;

end;



{特效变换}

procedure TForm1.TrackBar1Change(Sender: TObject);

var

  rGargle: TDSFXGargle;

begin

  if buf8 = nil then Exit;

  rGargle.dwRateHz := TrackBar1.Position;

  rGargle.dwWaveShape := RadioGroup1.ItemIndex;

  fxGargle.SetAllParameters(rGargle);

end;



procedure TForm1.FormDestroy(Sender: TObject);

begin

  buf8 := nil;

  myDSound := nil;

end;



end.



运行效果图:

在 Delphi 下使用 DirectSound (9): 效果器初步及 IDirectSoundFXGargle8 效果器

你可能感兴趣的:(Delphi)