通过 IDirectSoundBuffer 的 SetVolume、SetPan、SetFrequency、SetCurrentPosition 方法可以简单进行这些设置.
同时 IDirectSoundBuffer 也有对应的 GetVolume、GetPan、GetFrequency、GetCurrentPosition 方法.
关键的一点是如果能让缓冲区接受音量、相位和频率的设置, 必须在建立缓冲区时指定相应的标志.
下面的常量说明了它们的取值范围:
DSBVOLUME_MAX = 0; //音量最大值, 保持在控制面板设置的音量 DSBVOLUME_MIN = -10000; //音量最小值 DSBPAN_LEFT = -10000; //左声道 DSBPAN_CENTER = 0; //均衡 DSBPAN_RIGHT = 10000; //右声道 DSBFREQUENCY_ORIGINAL = 0; //使用默认 DSBFREQUENCY_MIN = 100; //频率最小值 DSBFREQUENCY_MAX = 200000; //频率最大值, 在 DirectSound 9.0 之下的版本, 此值是 100000
在上一个例子中, 最占篇幅的就是那两个函数; 为了更方便使用, 把它们做在了一个 TReadWaveFile 类里:
{实现 TReadWaveFile 类的单元} unit ReadWaveFile; interface uses Windows, Classes, SysUtils, MMSystem; type TReadWaveFile = class private FFileHandle: HMMIO; FFormat: TWaveFormatEx; FSize: DWORD; public constructor Create; destructor Destroy; override; function Open(FileName: string): Boolean; //打开文件并读取信息 function Read(pDest: Pointer; Size: DWORD): Boolean; //读出波形数据 property Format: TWaveFormatEx read FFormat; //读出格式数据 property Size: DWORD read FSize; //读出波形数据的大小 end; implementation { TReadWaveFile } constructor TReadWaveFile.Create; begin inherited; end; destructor TReadWaveFile.Destroy; begin if FFileHandle > 0 then mmioClose(FFileHandle, 0); inherited; end; function TReadWaveFile.Open(FileName: string): Boolean; var ckiRIFF,ckiFmt,ckiData: TMMCKInfo; begin Result := False; if not FileExists(FileName) then Exit; FFileHandle := mmioOpen(PChar(FileName), nil, MMIO_READ); if FFileHandle = 0 then Exit; ZeroMemory(@ckiRIFF, SizeOf(TMMCKInfo)); mmioDescend(FFileHandle, @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(FFileHandle, @ckiFmt, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then mmioRead(FFileHandle, @FFormat, SizeOf(TWaveFormatEx)); mmioAscend(FFileHandle, @ckiFmt, 0); if (mmioDescend(FFileHandle, @ckiData, @ckiRIFF, MMIO_FINDCHUNK) = MMSYSERR_NOERROR) then FSize := ckiData.cksize; Result := FFormat.wFormatTag = WAVE_FORMAT_PCM; end; function TReadWaveFile.Read(pDest: Pointer; Size: DWORD): Boolean; begin Result := mmioRead(FFileHandle, pDest, Size) = Size; end; end.
测试程序用到了四个 Button 和三个 TrackBar 还有它们的默认事件:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) Button1: TButton; //打开并播放 Button2: TButton; //反复播放 Button3: TButton; //暂停 Button4: TButton; //从头播放 TrackBar1: TTrackBar; //用于音量调节 TrackBar2: TTrackBar; //用于相位调节 TrackBar3: TTrackBar; //用于频率调节 procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure TrackBar1Change(Sender: TObject); procedure TrackBar2Change(Sender: TObject); procedure TrackBar3Change(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} uses DirectSound, ReadWaveFile; // ReadWaveFile 是 TReadWaveFile 类所在的单元 var myDSound: IDirectSound8; buf: IDirectSoundBuffer; procedure TForm1.FormCreate(Sender: TObject); begin TrackBar1.Min := DSBVOLUME_MIN; TrackBar1.Max := DSBVOLUME_MAX; TrackBar2.Min := DSBPAN_LEFT; TrackBar2.Max := DSBPAN_RIGHT; TrackBar3.Min := 100; TrackBar3.Max := 100000; Button1.Caption := '打开并播放'; Button2.Caption := '反复播放'; Button3.Caption := '暂停'; Button4.Caption := '从头播放'; System.ReportMemoryLeaksOnShutdown := true; end; procedure TForm1.Button1Click(Sender: TObject); var bufDesc: TDSBufferDesc; p1: Pointer; n1: DWORD; wavPath: string; wav: TReadWaveFile; // begin buf := nil; myDSound := nil; with TOpenDialog.Create(nil) do begin Filter := 'Wave File(*.wav)|*.wav'; if Execute then wavPath := FileName; Free; end; wav := TReadWaveFile.Create; if not wav.Open(wavPath) then begin ShowMessage('只能是 PCM 格式的 WAVE 文件'); wav.Free; Exit; end; DirectSoundCreate8(nil, myDSound, nil); myDSound.SetCooperativeLevel(Self.Handle, DSSCL_NORMAL); ZeroMemory(@bufDesc, SizeOf(TDSBufferDesc)); bufDesc.dwSize := SizeOf(TDSBufferDesc); {指定缓冲区允许音量、相位和频率调节} bufDesc.dwFlags := DSBCAPS_STATIC or DSBCAPS_CTRLVOLUME or DSBCAPS_CTRLPAN or DSBCAPS_CTRLFREQUENCY; 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); buf.Play(0, 0, 0); TrackBar1.Position := 0; TrackBar2.Position := 0; TrackBar3.Position := wav.Format.nSamplesPerSec; wav.Free; end; procedure TForm1.Button2Click(Sender: TObject); begin if buf <> nil then buf.Play(0, 0, DSBPLAY_LOOPING); end; procedure TForm1.Button3Click(Sender: TObject); begin if buf <> nil then buf.Stop; end; {从头播放} procedure TForm1.Button4Click(Sender: TObject); begin if buf = nil then Exit; buf.Stop; buf.SetCurrentPosition(0); buf.Play(0, 0, 0); end; {音量调节} procedure TForm1.TrackBar1Change(Sender: TObject); begin if buf <> nil then buf.SetVolume(TTrackBar(Sender).Position); end; {相位调节} procedure TForm1.TrackBar2Change(Sender: TObject); begin if buf <> nil then buf.SetPan(TTrackBar(Sender).Position); end; {频率调节} procedure TForm1.TrackBar3Change(Sender: TObject); begin if buf <> nil then buf.SetFrequency(TTrackBar(Sender).Position); end; procedure TForm1.FormDestroy(Sender: TObject); begin buf := nil; myDSound := nil; end; end.