zoukankan      html  css  js  c++  java
  • 在 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.
    


    运行效果图:


  • 相关阅读:
    hdoj2187:悼念512汶川大地震遇难同胞 (贪心)
    2.0其它之Transform详解,以及UIElement和FrameworkElement的常用属性
    2.0外观之样式, 模板, 视觉状态和视觉状态管理器
    2.0图形之Ellipse, Line, Path, Polygon, Polyline, Rectangle
    2.0控件之ListBox, MediaElement, MultiScaleImage, PasswordBox, ProgressBar, RadioButton
    2.0画笔之SolidColorBrush, ImageBrush, VideoBrush, LinearGradientBrush, RadialGradientBrush
    2.0图形之基类System.Windows.Shapes.Shape
    2.0交互之鼠标事件和键盘事件
    2.0控件之ScrollViewer, Slider, StackPanel, TabControl, TextBlock, TextBox, ToggleButton
    2.0交互之InkPresenter(涂鸦板)
  • 原文地址:https://www.cnblogs.com/del/p/1941193.html
Copyright © 2011-2022 走看看